Saturday, June 28, 2008

Junit 2

What Happens If a JUnit Test Method Is Declared as "private"?

If a JUnit test method is declared as "private", the compilation will pass ok. But the execution will fail. This is decause JUnit requires that all test methods must be declared as "public". For example:

type HelloTestPrivate.java

import org.junit.Test;
import static org.junit.Assert.*;
// by FYICenter.com
public class HelloTestPrivate {
@Test private void testHello() {
String message = "Hello World!";
assertEquals(12, message.length());
}
}

javac -cp junit-4.4.jar HelloTestPrivate.java

java -cp .;junit-4.4.jar org.junit.runner.JUnitCore
HelloTestPrivate
JUnit version 4.4
.E
Time: 0
There was 1 failure:
1) initializationError0(HelloTestPrivate)
java.lang.Exception: Method testHello should be public
at org.junit.internal.runners.MethodValidator.validateTestMethod
at org.junit.internal.runners.MethodValidator.validateInstanceMe
at org.junit.internal.runners.MethodValidator.validateMethodsFor
at org.junit.internal.runners.JUnit4ClassRunner.validate(JUnit4C
at org.junit.internal.runners.JUnit4ClassRunner.(JUn
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeC
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Del
at java.lang.reflect.Constructor.newInstance(Constructor.java:51
at org.junit.internal.requests.ClassRequest.buildRunner(ClassReq
at org.junit.internal.requests.ClassRequest.getRunner(ClassReque
at org.junit.internal.requests.ClassesRequest.getRunner(ClassesR
at org.junit.runner.JUnitCore.run(JUnitCore.java:109)
at org.junit.runner.JUnitCore.run(JUnitCore.java:100)
at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81)
at org.junit.runner.JUnitCore.main(JUnitCore.java:44)

FAILURES!!!
Tests run: 1, Failures: 1

What Happens If a JUnit Test Method Is Declared to Return "String"?

If a JUnit test method is declared to return "String", the compilation will pass ok. But the execution will fail. This is decause JUnit requires that all test methods must be declared to return "void". For example:

type HelloTestNonVoid.java import org.junit.Test; import static org.junit.Assert.*; // by FYICenter.com public class HelloTestNonVoid { @Test public String testHello() { String message = "Hello World!"; assertEquals(12, message.length()); return message; } } javac -cp junit-4.4.jar HelloTestNonVoid.java java -cp .;junit-4.4.jar org.junit.runner.JUnitCore HelloTestNonVoid JUnit version 4.4 .E Time: 0 There was 1 failure: 1) initializationError0(HelloTestNonVoid) java.lang.Exception: Method testHello should be void at org.junit.internal.runners.MethodValidator.validateTe at org.junit.internal.runners.MethodValidator.validateIn at org.junit.internal.runners.MethodValidator.validateMe at org.junit.internal.runners.JUnit4ClassRunner.validate at org.junit.internal.runners.JUnit4ClassRunner.(J at sun.reflect.NativeConstructorAccessorImpl.newInstance at sun.reflect.NativeConstructorAccessorImpl.newInstance at sun.reflect.DelegatingConstructorAccessorImpl.newInst at java.lang.reflect.Constructor.newInstance(Constructor at org.junit.internal.requests.ClassRequest.buildRunner( at org.junit.internal.requests.ClassRequest.getRunner(Cl at org.junit.internal.requests.ClassesRequest.getRunner( at org.junit.runner.JUnitCore.run(JUnitCore.java:109) at org.junit.runner.JUnitCore.run(JUnitCore.java:100) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81) at org.junit.runner.JUnitCore.main(JUnitCore.java:44) FAILURES!!! Tests run: 1, Failures: 1

Why Does Poeple Import org.junit.Assert Statically?

Poeple use the static import statement on org.junit.Assert to save coding time on calling its assetion methods. With a normal import statement, assertion method names must qualified with the class name like this:

import org.junit.Assert; ... Assert.assertEquals(12, message.length());

With a static import statement, assertion method names can be used directly like this:

import static org.junit.Assert.*; ... assertEquals(12, message.length());

How To Group Multiple Test Classes into a Suite in JUnit 4.4?

JUnit 4.4 stops using the "public static Test suite()" method to build a test suite class. It is now provides the org.junit.runners.Suite class and two annotations to help you to build test suite.

org.junit.runners.Suite - JUnit 4.4 runner class that runs a group of test classes.

org.junit.runner.RunWith - JUnit 4.4 class annotation that specify runner class to run the annotated class.

org.junit.runner.Suite.SuiteClasses - JUnit 4.4 class annotation that specify an array of test classes for the Suite.class to run.

The annotated class should be an empty class.

To run "@Suite.SuiteClasses" class, you can use the core runner: org.junit.runner.JUnitCore.

Here is a good example of "@Suite.SuiteClasses" class:

import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; // by FYICenter.com // specify a runner class: Suite.class @RunWith(Suite.class) // specify an array of test classes @Suite.SuiteClasses({ HelloTest.class, ExpectedExceptionTest1.class, ExpectedExceptionTest2.class, UnexpectedExceptionTest1.class, UnexpectedExceptionTest2.class} ) // the actual class is empty public class AllTests { }

How To Run a "@Suite.SuiteClasses" Class in JUnit 4.4?

If you define create "@Suite.SuiteClasses" class as described in the previous question, you run it with the core runner: org.junit.runner.JUnitCore:

java -cp .;junit-4.4.jar org.junit.runner.JUnitCore AllTests JUnit version 4.4 ....E.E Time: 0.016 There were 2 failures: 1) testGet(UnexpectedExceptionTest1) java.lang.AssertionError: Unexpected exception at org.junit.Assert.fail(Assert.java:74) at UnexpectedExceptionTest1.testGet(UnexpectedExceptionTest1.java:13) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ... at org.junit.runner.JUnitCore.run(JUnitCore.java:100) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81) at org.junit.runner.JUnitCore.main(JUnitCore.java:44) 2) testGet(UnexpectedExceptionTest2) java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 at java.util.ArrayList.RangeCheck(ArrayList.java:547) at java.util.ArrayList.get(ArrayList.java:322) at UnexpectedExceptionTest2.testGet(UnexpectedExceptionTest2.java:13) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ... at org.junit.runner.JUnitCore.run(JUnitCore.java:100) at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81) at org.junit.runner.JUnitCore.main(JUnitCore.java:44) FAILURES!!! Tests run: 5, Failures: 2

What Is the "@SuiteClasses" Annotation?

"@SuiteClasses" is a class annotation defined in JUnit 4.4 in org.junit.runners.Suite.SuiteClasses. It allows you to define a suite class as described in the previous question.

By the way, the API document of JUnit 4.4 has a major typo for the org.junit.runners.Suite class (Suite.html).

Using Suite as a runner allows you to manually build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x static Test suite() method. To use it, annotate a class with @RunWith(Suite.class) and @SuiteClasses(TestClass1.class, ...). When you run this class, it will run all the tests in all the suite classes.

"@SuiteClasses(TestClass1.class, ...)" should be changed to "@Suite.SuiteClasses({TestClass1.class, ...})".

Someone provided wrong information on build test suite in JUnit 4.4. Do not follow this:

JUnit provides tools to define the suite to be run and to display its results. To run tests and see the results on the console, run:

org.junit.runner.TextListener.run(TestClass1.class, ...);


Why Not Just Write a main() Method for Unit Testing?

It is possible to write a main() method in each class that need to be tested for unit testing. In the main() method, you could create test object of the class itself, and write some tests to test its methods.

However, this is not a recommended approach because of the following points:

  • Your classes will be cluttered with test code in main method. All those test codes will be packaged into the final product.
  • If you have a lots of classes to test, you need to run the main() method of every class. This requires some extra coding effort.
  • If you want the test results to be displayed in a GUI, you will have to write code for that GUI.
  • If you want to log the results of tests in HTML format or text format, you will have to write additional code.
  • If one method call fails, next method calls won?t be executed. You will have to work-around this.
  • If you start working on a project created by some other team in your organization, you may see an entirely different approach for testing. That will increase your learning time and things won?t be standard.

Above are some of the problems, which will be taken care of automatically if you use Junit. Junit provides a standard framework for writing your tests. It separates test code from project code, hence keeps everything clean.


Why Not Just Use System.out.println() for Unit Testing?

Inserting debug statements into code is a low-tech method for debugging it. It usually requires that output be scanned manually every time the program is run to ensure that the code is doing what's expected.

It generally takes less time in the long run to codify expectations in the form of an automated JUnit test that retains its value over time. If it's difficult to write a test to assert expectations, the tests may be telling you that shorter and more cohesive methods would improve your design.

Under What Conditions Should You Test set() and get() Methods?

This is a good question for a job interview. It shows your experience with test design and data types.

Tests should be designed to target areas that might break. set() and get() methods on simple data types are unlikely to break. So no need to test them.

set() and get() methods on complex data types are likely to break. So you should test them.

Under What Conditions Should You Not Test Get() and Set() Methods?

The JUnit FAQ provides a good answer to this question:

Most of the time, get/set methods just can't break, and if they can't break, then why test them? While it is usually better to test more, there is a definite curve of diminishing returns on test effort versus "code coverage". Remember the maxim: "Test until fear turns to boredom."

Assume that the getX() method only does "return x;" and that the setX() method only does "this.x = x;". If you write this test:

    @Test
public void testGetSetX() {
setX(23);
assertEquals(23, getX());
}

then you are testing the equivalent of the following:

    @Test
public void testGetSetX() {
x = 23;
assertEquals(23, x);
}

or, if you prefer,

    @Test
public void testGetSetX() {
assertEquals(23, 23);
}

At this point, you are testing the Java compiler, or possibly the interpreter, and not your component or application. There is generally no need for you to do Java's testing for them.

If you are concerned about whether a property has already been set at the point you wish to call getX(), then you want to test the constructor, and not the getX() method. This kind of test is especially useful if you have multiple constructors:

    @Test

public void testCreate() {
assertEquals(23, new MyClass(23).getX());
}

No comments:

Topics