An Introduction to JUnit

Topics

  1. Introduction to JUnit 2. Where to get JUnit 3. Running the Examples 4. JUnit API 5. Creating your First Test (Command Line and Eclipse) 6. Fixtures 7. Suites 8. More about Assertions 9. JUnit tests and the Derby test harness 10. Resources

Introduction to JUnit

Write tests during development

Code a little, test a little, ad infinitum

Provides the structure of a test case and tools

Test Case: junit.framework.TestCase

Tools: junit.textui.TestRunner, junit.awtui.TestRunner, junit.swingui.TestRunner

When writing test cases:

  1. Create the objects 2. Exercise the objects 3. Verify the results

For more info on the background of JUnit, see JUnit Test Infected: Programmers Love Writing Tests in the installed documentation: junit3.8.2/doc/testinfected/testing.htm.

Where to get JUnit

http://prdownloads.sourceforge.net/junit/

The Derby tests have been run against 3.8.1 and 3.8.2, however 3.8.2 gives more context in the reporting of failures and errors.

  1. Download junit3.8.2.zip 2. Unzip it 3. Includes documentation and javadoc (API)

Example of 3.8.2 improvement:
expected:<...st of the emergency [broadcasting] system> but was:<...st of the emergency [locating] system>

Running the Examples

%JUNIT_HOME%/junit/samples
Add junit.jar to your class path 

C:\junit\junit3.8.2>set CLASSPATH=junit.jar;.

C:\junit\junit3.8.2>java junit.textui.TestRunner junit.samples.AllTests
.........................................
.........................................
.........................................
........
Time: 0.811

OK (131 tests)

JUnit API

junit.framework.Test Interface has two methods

  • countTestCases() - Counts the number of test cases this test (java class) will run
  • run(TestResult) - Runs a test and collects its result in a TestResult instance

junit.framework.Assert - contains multiple static assert methods

  • assertEquals() methods with different parameters (overloaded method)
  • assertFalse()
  • assertNotNull()
  • assertNotSame()
  • assertNull()
  • assertSame()
  • assertTrue(boolean)
  • fail(String message)

junit.framework.TestCase implements Test and extends Assert - contains convenience methods:

  • runTest() - the test(s) to run
  • setUp() - initialize test variables, open network connections, etc.
  • tearDown() - closes resources

Your class must extend junit.framework.TestCase.

Creating your First Test (Command Line and Eclipse)

  1. Implement a subclass of TestCase (extend TestCase) 2. Override the method runTest(), calling the tests to run 3. Create a method to test, using one of the assertXXX methods to compare test values

The method to test must start with the prefix 'test', e.g. 'testAddMethod'.

Example of the simplest test case, containing only one test:

package junitdemo;

import junit.framework.TestCase;

public class FirstJUnitTest extends TestCase {

	protected void runTest() {
		testAdd();
	}

	public void testAdd() {
		int val1 = 5;
		int val2 = 6;
		int result = val1 + val2;
             // assertTrue method takes a boolean
		assertTrue(result == 11);
	}

}

To test this class via the Command Line

  1. make sure junit.jar is in your class path and the directory where the compiled classes reside 2. compile the test 3. run the test using the tool junit.textui.TestRunner
C:\junit\junit3.8.2>set CLASSPATH=C:\junit\junit3.8.2\junit.jar;classes

C:\junit\junit3.8.2> javac -d classes src/junitdemo/FirstJUnitTest.java 
   (places the compiled class in the classes directory)

C:\junit\junit3.8.2>java junit.textui.TestRunner junitdemo.FirstJUnitTest
.
Time: 0

OK (1 test) 

To create and test this class via Eclipse

  1. While in the Java Perspective, right click in the Package Explorer View, New >> Project >> Java Project, Provide a name >> Click Next. In the Java Settings screen select the Libraries tab, then click the Add External JARs button.
    2. Browse to junit.jar, then click Finish.
    3. Right click the Project, New >> JUnit Test Case, package = junitedemo, class name=FirstJUnitTest, Superclass = junit.framework.TestCase, don't select any of the method stubs be created, leave the Class under test blank, click 'Finish'. 4. Add the two methods shown above, runTest() and testAdd(), and save the file.
    5. To run the test right click FirstJUnitTest.java, Run As >> JUnit Test OR click the icon on the Menu Bar with the green circle and white arrow, Run As >> JUnit Test.
    6. A new view in Eclipse will appear as a JUnit tab and show if there were any Errors or Failures.

Fixtures

The preceding example ran one test in one test class. It's not very efficient to build a test like that so let's create a Fixture of tests next.

A Fixture is used if you have more than one test that will operate on the same or similar sets of objects.

For a single test (method) contained in a single Test class these were the steps we needed to do:

  1. Create a subclass of TestCase (extend TestCase) 2. Override the method runTest(), calling the tests to run 3. Create a method to test, using one of the assertXXX methods to compare test values

For a fixture use these steps:

  1. Create a subclass of TestCase (extend TestCase) 2. Add an instance variable for each part of the fixture 3. Override setUp() to initialize the variables 4. Override tearDown() to release any permanent resources you allocated in setUp()

Below is an example of a Fixture class which contains five tests to run. (In reality you could have collapsed these tests into a fewer number.)

If you are using Eclipse to create this class, go ahead and select the main() and setUp() method stubs be created this time. For the main() method, select 'Add TestRunner statement for: text ui'.

For this example we'll show how to run a JUnit test case with a main() in it instead of having to use the tool, junit.textui.TestRunner class, for our main class.

JUnit Fixture class with a Main method

package junitdemo;

import junit.framework.TestCase;

public class FixtureJUnitTest extends TestCase {

	protected int val1;

	protected int val2;

	protected String s1;

	protected String s2;

	public static void main(String[] args) {
		junit.textui.TestRunner.run(FixtureJUnitTest.class);
	}

	protected void setUp() throws Exception {
		super.setUp();
		val1 = 5;
		val2 = 6;
		s1 = "string1";
		s2 = new String("string1");
	}

	public void testAdd() {
		int result = val1 + val2;
		assertTrue(result == 11);
	}

	public void testStringEqual1() {
		// test to see if string s1 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s1);
	}

	public void testStringEqual2() {
		// test to see if string s2 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s2);
	}

	public void testStringSameObject1() {
		// assertSame(message, Object expected, Object actual)
		// should succeed because "string1".equals(s1)
		assertSame("Fail: string1 is not the same as s1", "string1", s1);
	}

	public void testStringSameObject2() {
		// should fail because "string1" not equal to new String("string1")
		assertSame("Fail: string1 is not the same as s2", "string1", s2);
	}

}

To run this class via Eclipse select Run As >> Java Application.

To run this class via the Command Line:

Set the class path:

  1. C:\junit\junit3.8.2>set CLASSPATH=C:\junit\junit3.8.2\junit.jar;classes

Compile the class:

1.#2 C:\junit\junit3.8.2>javac -d classes src/junitdemo/FixtureJUnitTest.java

Run the class (since there is a main method, just run it normally):

C:\junit\junit3.8.2>java junitdemo.FixtureJUnitTest
.....F
Time: 0
There was 1 failure:
1) testStringSameObject2(junitdemo.FixtureJUnitTest)junit.framework.AssertionFai
ledError: 
        Fail: string1 is not the same as s2 expected same:<string1> was not:<string1>
        at junitdemo.FixtureJUnitTest.testStringSameObject2(FixtureJUnitTest.java:52)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
        at junitdemo.FixtureJUnitTest.main(FixtureJUnitTest.java:16)

FAILURES!!!
Tests run: 5,  Failures: 1,  Errors: 0

Looking at the output you can see that our test called testStringSameObject2 failed with our custom message: Fail: string1 is not the same as s2.

JUnit added: expected same:<string1> was not:<string1>, and the stack trace shows the line number of the failure as well.

If you prefer to create your fixture classes without a main (which is probably the case if you will be integrating them into the derby harness) just do the following;

  1. remove the main method from FixtureJUnitTest 2. run the class with the junit.textui.TestRunner class like before
C:\junit\junit3.8.2>java junit.textui.TestRunner junitdemo.FixtureJUnitTest

Suites

A TestSuite is the smallest execution unit in JUnit.

To run all of the tests in a class do the following:

  1. Create a one arg constructor which takes a String - the name of the method to run 2. create a suite() method which returns an implementation of the Test Interface, e.g. TestSuite

The class below, SuiteJUnitTest.java demonstrates this.

Running a Suite of Tests

package junitdemo;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestResult;
import junit.framework.TestSuite;

public class SuiteJUnitTest extends TestCase {

	protected int val1;

	protected int val2;

	protected String s1;

	protected String s2;
	
	public SuiteJUnitTest(String arg0) {
		super(arg0);
	}
	
      // to run all tests in this class

	public static Test suite() {
		return new TestSuite(SuiteJUnitTest.class);
	}
	
	protected void setUp() throws Exception {
		super.setUp();
		val1 = 5;
		val2 = 6;
		s1 = "string1";
		s2 = new String("string1");
	}

	public void testAdd() {
		int result = val1 + val2;
		assertTrue(result == 11);
	}

	public void testStringEqual1() {
		// test to see if string s1 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s1);
	}

	public void testStringEqual2() {
		// test to see if string s2 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s2);
	}

	public void testStringSameObject1() {
		// assertSame(message, Object expected, Object actual)
		// should succeed because "string1".equals(s1)
		assertSame("Fail: string1 is not the same as s1", "string1", s1);
	}

	public void testStringSameObject2() {
		// should fail because "string1" not equal to new String("string1")
		assertSame("Fail: string1 is not the same as s2", "string1", s2);
	}

}

If you want to specifically name which methods to run in the suite (for instance if you don't want to run all of them) change the above suite() method to this:

	// only adds two tests to the suite
	public static Test suite() {
		TestSuite suite = new TestSuite();
		suite.addTest(new SuiteJUnitTest("testAdd"));
		suite.addTest(new SuiteJUnitTest("testStringEqual1"));
		return suite;
	}

The output from running the modified SuiteJUnitTest class, which only runs two tests is shown below;

C:\junit\junit3.8.2>java junit.textui.TestRunner junitdemo.SuiteJUnitTest
..
Time: 0

OK (2 tests)

Creating Suites from different Java classes

You can also create a class that acts as sort of a harness that runs suites from two or more classes. An example of this is shown below. Also, you do not have to create a special class to run tests from other classes, you could have one class that adds tests from it's own suite and adds tests from another class too.

package junitdemo;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestResult;
import junit.framework.TestSuite;

public class SuiteHarnessJUnitTest extends TestCase {
	
	public SuiteHarnessJUnitTest(String arg0) {
		super(arg0);
	}
	
	// Adds two suites to this suite
	public static Test suite() {
		TestSuite suite = new TestSuite();
		// the SuiteTest1JUnitTest class has two tests in it's suite
		suite.addTest(SuiteTest1JUnitTest.suite());
		// the SuiteTest2JUnitTest class has one test in it's suite
		// so a total of three tests will be run
		suite.addTest(SuiteTest2JUnitTest.suite());
		return suite;
	}
	
}

SuiteTest1JUnitTest

package junitdemo;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class SuiteTest1JUnitTest extends TestCase {

	protected String s1;

	protected String s2;

	public SuiteTest1JUnitTest(String arg0) {
		super(arg0);
	}

	public static Test suite() {
		TestSuite suite = new TestSuite();
		// only add two tests from this class to the suite
		suite.addTest(new SuiteTest1JUnitTest("testStringEqual2"));
		suite.addTest(new SuiteTest1JUnitTest("testStringEqual1"));
		return suite;
	}

	protected void setUp() throws Exception {
		super.setUp();
		s1 = "string1";
		s2 = new String("string1");
	}

	public void testStringEqual1() {
		// test to see if string s1 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s1);
	}

	public void testStringEqual2() {
		// test to see if string s2 is equal to "string1"
		//assertEquals(String expected, String actual)
		assertEquals("string1", s2);
	}

	public void testStringSameObject1() {
		// assertSame(message, Object expected, Object actual)
		// should succeed because "string1".equals(s1)
		assertSame("Fail: string1 is not the same as s1", "string1", s1);
	}

	public void testStringSameObject2() {
		// should fail because "string1" not equal to new String("string1")
		assertSame("Fail: string1 is not the same as s2", "string1", s2);
	}

}

SuiteTest2JUnitTest

package junitdemo;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class SuiteTest2JUnitTest extends TestCase {

	protected int val1;

	protected int val2;

	public SuiteTest2JUnitTest(String arg0) {
		super(arg0);
	}

	public static Test suite() {
		TestSuite suite = new TestSuite();
		suite.addTest(new SuiteTest2JUnitTest("testSubtract"));
		return suite;
	}

	protected void setUp() throws Exception {
		super.setUp();
		val1 = 5;
		val2 = 6;
	}

	public void testSubtract() {
		int result = val2 - val1;
		assertTrue(result == 1);
	}

}

More about Assertions

There are several assertXXX methods, and the examples so far have not used very many of them. Check out the javadoc that comes with JUnit to get a more thorough explanation of these methods.

JUnit_Install_Dir/javadoc/junit/framework/Assert.html

Several of these are overloaded to take a String message to customize the error message, we saw that a little bit earlier.

Below are some code snippets that show examples of using the various assert methods.

assertEquals

Overloaded for almost all primitive data types and the Object arguments.

double expected = java.lang.StrictMath.acos(testArcValues[i]);
double rValue = executeValues("ACOS", testArcValues[i]);
assertEquals(expected, rValue, 0.0);

assertTrue

Takes a boolean argument. Use this when the expected result is true.

// verify that autocommit is on
assertTrue(getConnection().getAutoCommit());

assertFalse

Takes a boolean argument. Use this when the expected result is false.

// there should be no more rows in this ResultSet
assertFalse(rs.next());

assertNull

Takes an object with an optional second argument, the String message. Use this when you expect an object to be null.

// expecting SQL NULL in column 2 of this row
assertNull("Second column should be null", rs.getString(2));

assertNotNull

Takes an object with an optional second argument, the String message. Use this when you want to accept anything but null.

String importantText = rs.getString(1);
assertNotNull("Fail: Important text is null! ", importantText);

assertNotSame

Takes two objects with an optional third argument, the String message.

s2 = new String("string1");
assertNotSame("Fail: string1 is the same as s2", "string1", s2)

fail

Fail takes one argument, the String message output, and is used to fail a test.

Why would you want to use this? For instance if you are performing some action that should throw an exception, and you want the test to fail if no exception was thrown.

try {
     // for the value TWO_RADIANS, this method should throw
     // an SQLException in Derby
     executeValues("ACOS", TWO_RADIANS);
     fail("ACOS: Out of range test failed, input value is: "
					+ TWO_RADIANS);
} catch (SQLException sqlE) {
     // "ERROR 22003: The resulting value is outside the range for the data type DOUBLE.";
     assertSQLState(SQLStateConstants.DATA_EXCEPTION_NUMERIC_VALUE_OUT_OF_RANGE, sqlE);
}

JUnit tests and the Derby test harness

There are changes being made to the way the Derby test harness uses JUnit, for more info refer to these pages on the wiki:

Resources

JUnit Docs: Installed with JUnit zip file, also available at JUnit.

JUnit FAQ

The Derby wiki:

Eclipse JUnit Getting Started:

  • Open Eclipse Help from the main menu item, select the Java Development User Guide book, Getting Started >> Basic tutorial, Writing and running JUnit tests.
  • Eclipse JUnit Tutorial

Other articles:

Tools:

  • No labels