Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Get Acquainted with the New Advanced Features of JUnit 4 : Page 2

Learn how to migrate from JUnit 3.8 to JUnit 4. Discover version 4's new features, including extensive use of annotations, and find out the status on IDE integration.


advertisement
Migrating a Test Class
Now I'll take a simple test class already written in JUnit 3.8 and migrate it to JUnit 4. This class has some flaws: It does not test all the business methods and it looks like there is a bug in the testDivide method (8/2 is not equal to 5). Because the implementation of multiply is not ready, its test is written but ignored.

The differences between the two frameworks are highlighted in bold (see Table 1).

Table 1. CaculatorTest in JUnit 3.8 and JUnit 4

JUnit 3.8 JUnit 4


package junit3;

import calc.Calculator;
import junit.framework.TestCase;




public class CalculatorTest extends TestCase {

private static Calculator calculator = new Calculator();

@Override
protected void setUp() {
calculator.clear();
}


public void testAdd() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(), 2);
}


public void testSubtract() {
calculator.add(10);
calculator.subtract(2);
assertEquals(calculator.getResult(), 8);
}


public void testDivide() {
calculator.add(8);
calculator.divide(2);
assert calculator.getResult() == 5;
}

public void testDivideByZero() {
try {
calculator.divide(0);
fail();
} catch (ArithmeticException e) {
}
}

public void notReadyYetTestMultiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(), 100);
}
}

package junit4;

import calc.Calculator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {

private static Calculator calculator = new Calculator();

@Before
public void clearCalculator() {
calculator.clear();
}

@Test
public void add() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(), 2);
}

@Test
public void subtract() {
calculator.add(10);
calculator.subtract(2);
assertEquals(calculator.getResult(), 8);
}

@Test
public void divide() {
calculator.add(8);
calculator.divide(2);
assert calculator.getResult() == 5;
}

@Test(expected = ArithmeticException.class)
public void divideByZero() {
calculator.divide(0);
}


@Ignore("not ready yet")
@Test
public void multiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(), 100);
}
}


Packages
First of all, you can see that JUnit 4 uses org.junit.* package while JUnit 3.8 uses junit.framework.*. Of course, for backward compatibility, the JUnit 4 jar file ships with both packages.

Inheritance
Test classes do not have to extend junit.framework.TestCase anymore. In fact, they don't have to extend anything. JUnit 4 has decided to use annotations instead. To be executed as a test case, a JUnit 4 class needs at least one @Test annotation. For example, if you write a class with only @Before and @After annotations without at least one @Test method, you will get an error when trying to execute it: java.lang.Exception: No runnable methods.

Assert Methods
Because in JUnit 4 a test class doesn't inherit from TestCase (where assertEquals() methods are defined in JUnit 3.8), you have to use the prefixed syntax (e.g. Assert.assertEquals()) or, thanks to JDK 5.0, import statically the Assert class. In doing so, you can then use assertEquals methods exactly the way you have used them previously (e.g. assertEquals ()).

There are two new assertion methods in JUnit 4. They are used to compare arrays of objects. Both arrays are equal if each element they contain is equal.

public static void assertEquals(String message, Object[] expecteds, Object[] actuals); public static void assertEquals(Object[] expecteds, Object[] actuals);

Twelve assertEquals methods have totally disappeared thanks to the autoboxing of the JDK 5.0. Methods such as assertEquals(long, long) in JUnit 3.8 use the assertEquals(Object, Object) in JUnit 4. It is the same for assertEquals(byte, byte), assertEquals(int, int), and so on. This will help prevent the antipattern "Using the Wrong Assert" (see http://www-128.ibm.com/developerworks/opensource/library/os-junit/ and http://www.exubero.com/junit/antipatterns.html ).

A novelty with JUnit 4 is the neat integration of the assert keyword (divide() method in our example). You can use it as you would use the assertEquals methods, because they both throw the same exception (java.lang.AssertionError). JUnit 3.8 assertEquals would throw a junit.framework.AssertionFailedError. Note that when using assert, you must specify the –ea parameter to Java; if not, asserts are ignored (see "Programming with Asserts").

Fixture
Fixtures are methods to initialize and release any common objects during tests. In JUnit 3.8 you would use setUp() for initialization before running each test and then tearDown() for cleaning purposes after each test had completed. Both methods are overridden from the TestCase class and therefore are uniquely defined. Note that I'm using the Java 5.0 built-in @Override annotation for the setup method—this annotation indicates that the method declaration is intended to override the method declaration in a superclass. Instead, JUnit 4 uses @Before and @After annotations. These methods can be called by any name (clearCalculator() in our example). I'll explain more about these annotations later in the article.

Tests
JUnit 3.8 recognizes a test method by analyzing its signature: The method name has to be prefixed with 'test', it must return void, and it must have no parameters (e.g. public void testDivide()). A test method that doesn't follow this naming convention is simply ignored by the framework and no exception is thrown, indicating a mistake has been made.

JUnit 4 doesn't use the same conventions. A test method does not have to be prefixed with 'test' but instead uses the @Test annotation. As in the previous framework, a test method must return void and have no parameters. With JUnit 4 this is controlled at runtime and throws an exception if not respected:

java.lang.Exception: Method xxx should have no parameters java.lang.Exception: Method xxx should be void

The @Test annotation supports the optional expected parameters. It declares that a test method should throw an exception. If it doesn't or if it throws a different exception than the one declared, the test fails. In our example, dividing an integer by zero should raise an ArithmeticException.

Ignoring a Test
Remember that the multiply method is not implemented. However, you don't want the test to fail, you just want it ignored. How do you temporarily disable a test with JUnit 3.8? By commenting it or changing the naming conventions so that the runner doesn't find it. In my example I used the method name notReadyYetTestMultiply(). It doesn't start with 'test' so it won't be recognized. The problem is that in the middle of thousands of other tests, you might not remember to rename this method.

To ignore a test in JUnit 4 you can comment a method or delete the @Test annotation (you can't change the naming conventions anymore or an exception would be thrown). However, the problem will remain: The runner will not report such a test. You can now add the @Ignore annotation in front or after @Test. Test runners will report the number of ignored tests, along with the number of tests that ran and the number of tests that failed. Note that @Ignore takes an optional parameter (a String) if you want to record why a test is being ignored.

Running the Tests
In JUnit 3.8 you could choose from several runners: text, AWT, or Swing. JUnit 4 only uses text runners. Remember the tagline underneath the JUnit logo? "Keep the bar green to keep the code clean." Well, JUnit 4 will not display any green bar to inform you that your tests have succeeded. If you want to see any kind of green you'll need to use JUnit extensions or an IDE that integrates JUnit such as IDEA or Eclipse"

First, I want to run the JUnit 3.8 test class with the good old junit.textui.TestRunner (with the –ea parameter to take into account the assert keyword).

java -ea junit.textui.TestRunner junit3.CalculatorTest ..F.E. There was 1 error: 1) testDivide(junit3.CalculatorTest)java.lang.AssertionError at junit3.CalculatorTest.testDivide(CalculatorTest.java:33) There was 1 failure: 1) testSubtract(junit3.CalculatorTest)junit.framework.AssertionFailedError: expected:<9> but was:<8> at junit3.CalculatorTest.testSubtract(CalculatorTest.java:27) FAILURES!!! Tests run: 4, Failures: 1, Errors: 1

TestDivide produces an error because assert ensures that 8/2 does not equal 5. TestSubstract produces a failure because 10-2 should be equal to 8 but there is a bug in the implementation and it returns 9.

Now I'll run both classes with the new org.junit.runner.JUnitCore runner, which acts like a facade for running tests. It can execute JUnit 4 and JUnit 3.8 tests as well as a mixture of both.

java –ea org.junit.runner.JUnitCore junit3.CalculatorTest JUnit version 4.1 ..E.E. There were 2 failures: 1) testSubtract(junit3.CalculatorTest) junit.framework.AssertionFailedError: expected:<9> but was:<8> at junit.framework.Assert.fail(Assert.java:47) 2) testDivide(junit3.CalculatorTest) java.lang.AssertionError at junit3.CalculatorTest.testDivide(CalculatorTest.java:33) FAILURES!!! Tests run: 4, Failures: 2

***

java –ea org.junit.runner.JUnitCore junit4.CalculatorTest JUnit version 4.1 ...E.EI There were 2 failures: 1) subtract(junit4.CalculatorTest) java.lang.AssertionError: expected:<9> but was:<8> at org.junit.Assert.fail(Assert.java:69) 2) divide(junit4.CalculatorTest) java.lang.AssertionError at junit4.CalculatorTest.divide(CalculatorTest.java:40) FAILURES!!! Tests run: 4, Failures: 2

The first visible difference is that the JUnit version number is displayed in the console (4.1). The second is that JUnit 3.8 differentiates failures and errors. JUnit 4 makes it simpler by only using failures. A novelty is the letter "I", which indicates that a test has been ignored.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap