12/30/07

JUnit 3.8 JUnit 4.x

Recently I looked through some JUnit 3.8 - JUnit 4 stuffs. Here are two useful readings in the subject: First is at IBM, other is on DevX. JUnit 4.X is basically direct improvement of JUnit 3.X with exploiting new features of JDK5. Current version is 4.4 and it can be downloaded from www.junit.org. What have been changed?
  • It needs (surprisingly) at least Java 5.
  • Package has been changed from junit.framework to org.junit.
  • Using @Test annotations instead of naming conventions. Methods must be public with void return type and they shouldn't have parameters. If we don't comply with these we would expect the following runtime exceptions:
    java.lang.Exception: Method xxx should have no parameters
    java.lang.Exception: Method xxx should be void

  • Using static import: import static org.junit.Assert.assertEquals; We can write instead of Assert.assertEquals(...); a sorter assertEquals(...);. (Of course we can use static import with JUnit 3.8 if we have Java 5.)
  • Extending TestCase isn't necessary anymore, so it becames possible to test protected methods by extending the subject class with the class which has the tests.
  • Instead of method setUp() we use @Before annotation, even more than one. Order of executing these methods is theoretically random.
  • tearDown()'s annotation pair is @After, which we can have also more than one.
  • In super classes we haven't have to invoke explicite setUp()==@Before and tearDown()==@After methods because their invocation is automatic: First @Before-s of the super class are invoked, then ones in descendant classes. Order of invoking @After is reversed. First the descendant classes, then super classes.
  • A @Before and @After is invoked before and after every test method as setUp() and tearDown() does. We have the possibility to concede @BeforeClass and @AfterClass which will be invoked before and after all test methods in the given class. There is no such feature in JUnit 3.X. (It has TestSuite class which ensures similar functionality.)
  • If a class doesn't have any @Test annotation, we will get an error.
  • In JUnit 3.X we could check exceptions by writing an assert into the catch block. In JUnit 4 we can define expected exceptions in annotation: @Test(expected=ArithmeticException.class). If exception isn't thrown or different exception has been thrown the test will fail. If more checks needed about parameters and message of the thrown exceptions we must follow the well-known try-catch practice.
  • If we don't want to run some test methods for some reason it's possible to ignore it by using @Ignore. (@Test needn't have to be removed. @Ignore can follow or precede it.) It can have a String-type parameter with the reason why is the test ignored. Test won't run and the runner will sign the fact that it was ignored. We can say @Ignore for the whole class but it's slightly differs from ignoring each test methods one by one, because @AfterClass and @BeforeClass will run in the latter case.
  • Highly appreciated feature to be able to give timeout for the test cases: @Test(timeout=500) Millisec.
  • There is a new assert which compares object arrays, however many (12) assert method removed because of the autoboxing feature. assertXXX(Object, Object) is used instead of them. More precisely DevX writes this happened but oddly I can use these old asserts in the TestCase class.
  • assert keyword of Java1.4 can also be used, however -ea JVM switch must be given at running the tests, otherwise asserts won't be evaluated. When using native asserts, instead of the JUnit's assertException, the general java.lang.AssertionError will raise in certain cases.
  • In JUnit4 there is no suite() method. We can create an empty class which has the runnable classes in annotation:
    @RunWith(Suite.class)
    @Suite.SuiteClasses({My1Test.class, My2Test.class, My2Test.class})
    public class AllTests {
    }

  • By using @RunWith class annotation we can define own runner for the test cases. For an example, the org.junit.runners.Parameterized, which drives the test by a set of parameters we defined previously. A public static method with @Parameters annotation is needed which returns a Collection and a public constructor is needed which can accept elements of the previous Collection. If the Collection contains integer pairs, constructor must have two integer parameters. The runner walks through on the Collection, calls the constructor and the test methods for every element of the Collection. DevX article has a good example about this on the third page. (Listing 2.)
  • Giving @RunWith(TestClassRunner.class) doesn't make any difference than giving anything because the TestClassRunner is the default runner.
  • JUnit4 doesn't make difference between expected failures and wrongly written test cases. This is a stepback. One testcase can have (passed/error/failure) in JUnit 3.8. In JUnit 4 it can have (passed/failure/(ignored)) only.
  • Test can be runned by java –ea org.junit.runner.JUnitCore. It can run 3.8 tests according to the DevX article. Practically 3.8 must have a small modification:
    public static junit.framework.Test suite() {
    return new JUnit4TestAdapter(MyTestClass.class);

    4.0 tests certainly won't run on 3.8. When having both old and new JUnit on the classpath they may impact. It least I had securityException.
  • I've heard about a so called assertThat method. Theoretically 4.4 have this but I didn't see it in the current javadoc on junit.org.
  • Eclipse 3.3 has JUnit4.4 and JUnit3.8 support.

If you need this in Hungarian, click here.

12/1/07

Logger frameworks

Let's have a look round in the world of logging.
There is a glorious article in the Wikipedia about fundamental concepts of logging.

Two (or rather three) competitors are log4j, java logging API == java.util.logging == JUL == JSR47 and Java Commons Logging == JCL. The latter is not a real logging framework, just an adapter which quests for the available framework in runtime and uses it. It can be helpful when we don't want to wrap some modul to log4j, JUL or anything, however, it has disadvantages regarding efficiency and functionality. See this article about it: Think again before adopting the commons-logging API

Here is an opinion about differences between log4j and JUL. I've read it a long time ago and deeply agree with it with the supplement, that because of the continuous inconveniences I hate Java Logging API.

(The author of the former article, Ceki Gülcü is the founder of log4j. He has a blog where he writes engaging things. For an example, mentions presentation of Linus Torvalds about GIT vs SVN. Well, I also didn't like that style not to say about this.)

Nowadays it seems that he (Ceki Gülcü) walks differents ways from Apache group, because he is working on the possible posteriors of log4j and JCL, namely LogBack and SLF4J. (Wikipedia article doesn't mentions these.) Theoretically, SLF4J will have log4j support. I don't know how much will be SLF4J efficient but I guess they started to implement a new framework because they wanted to make something better.

log4j exists since Java 1.1, JUL was invented in 1.4. By exploring homepage of log4j it turns out soon that there's no version which exploits advantages of Java 5.0. Log4j 2.0 would have been the one which fulfils this task, but there are not too much (any) visible activity in this subject.

However, LogBack and SLF4J are going to be in a useable shape soon. LogBack has version 0.9.X nowadays. On our Hungarian forum there was a flame about logBack and using isDebugEnabled. First important consequence was for me, that people do log in many different styles and it depends on the developed application. Somebody can use toString often, somebody doesn't have this possibility. Secondly, isDebugEnabled condition can't be dropped out in every cases, just when there are no expressions in the parameter list. (For sample, explicit high-cost function calls.) By the way, using varargs instead of String evaluation can be rather beneficial during logging.

Which one would I choose? In conservative software modules, when embedding environment may not be log4j-based I would choose JCL. Otherwise I would use log4j. At newly started projects I would use LogBack or SLF4J, although I would get a line if there are existing appenders which I should use. Regarding log4j, (as it is an old professional in the scene) there are a lot of appenders.

(This was a translation again. See original here in Hungarian.)