Stop Thinking, Just Do!

Sungsoo Kim's Blog

JUnit, A Tool for Test-Driven Development

tagsTags

11 January 2014


JUnit is a unit testing framework for the Java programming language. JUnit has been important in the development of test-driven development, and is one of a family of unit testing frameworks which is collectively known as xUnit that originated with SUnit. JUnit is linked as a JAR at compile-time; the framework resides under package junit.framework for JUnit 3.8 and earlier, and under package org.junit for JUnit 4 and later.

History

  • Kent Beck developed the first xUnit automated test tool for Smalltalk in mid-90’s
  • Beck and Gamma (of design patterns Gang of Four) developed JUnit on a flight from Zurich to Washington, D.C.
  • Martin Fowler

“Never in the field of software development was so much owed by so many to so few lines of code.”

  • Junit has become the standard tool for Test-Driven Development in Java (see Junit.org)
  • Junit test generators now part of many Java IDEs (Eclipse, BlueJ, Jbuilder, DrJava)
  • Xunit tools have since been developed for many other languages (Perl, C++, Python, Visual Basic, C#, …)

Why create a test suite?

  • Obviously you have to test your code—right?
    • You can do ad hoc testing (running whatever tests occur to you at the moment), or
    • You can build a test suite (a thorough set of tests that can be run at any time)
  • Disadvantages of a test suite
    • It’s a lot of extra programming
      • True, but use of a good test framework can help quite a bit
    • You don’t have time to do all that extra work
      • False! Experiments repeatedly show that test suites reduce debugging time more than the amount spent building the test suite
  • Advantages of a test suite
    • Reduces total number of bugs in delivered code
    • Makes code much more maintainable and refactorable

What is a testing framework?

  • A test framework provides reusable test functionality which:
    • Is easier to use (e.g. don’t have to write the same code for each class)
    • Is standardized and reusable
    • Provides a base for regression tests

Why use a testing framework?

  • Each class must be tested when it is developed
  • Each class needs a regression test
  • Regression tests need to have standard interfaces
  • Thus, we can build the regression test when building the class and have a better, more stable product for less work

Regression testing

  • New code and changes to old code can affect the rest of the code base
    • Affect’ sometimes means ‘break
  • We need to run tests on the old code, to verify it works – these are regression tests
  • Regression testing is required for a stable, maintainable code base

Architectural overview

  • JUnit test framework is a package of classes that lets you write tests for each method, then easily run those tests

  • TestRunner runs tests and reports TestResults
  • You test your class by extending abstract class TestCase
  • To write test cases, you need to know and understand the Assert class

Writing a TestCase

  • To start using JUnit, create a subclass of TestCase, to which you add test methods

  • Here’s a skeletal test class:

	import junit.framework.TestCase;
	
	public class TestBowl extends TestCase {
	} //Test my class Bowl
  • Name of class is important – should be of the form TestMyClass or MyClassTest
  • This naming convention lets TestRunner automatically find your test classes

Writing methods in TestCase

  • Pattern follows programming by contract paradigm:
    • Set up preconditions
    • Exercise functionality being tested
    • Check postconditions
  • Example:
	public void testEmptyList() {
		Bowl emptyBowl = new Bowl();
		assertEquals(Size of an empty list should be zero., 
			0, emptyList.size());
		assertTrue(An empty bowl should report empty.,			emptyBowl.isEmpty());
	}
  • Things to notice:
    • Specific method signature – public void testWhatever()
      • Allows them to be found and collected automatically by JUnit
    • Coding follows pattern
    • Notice the assert-type calls…

Assert methods

  • Each assert method has parameters like these: message, expected-value, actual-value
  • Assert methods dealing with floating point numbers get an additional argument, a tolerance
  • Each assert method has an equivalent version that does not take a message – however, this use is not recommended because:
    • messages helps documents the tests
    • messages provide additional information when reading failure logs
assertTrue(String message, Boolean test)
assertFalse(String message, Boolean test)
assertNull(String message, Object object)
assertNotNull(String message, Object object) 
assertEquals(String message, Object expected, Object actual) (uses equals method)
assertSame(String message, Object expected, Object actual) (uses == operator)
assertNotSame(String message, Object expected, Object actual)

More stuff in test classes

Suppose you want to test a class Counter

// This is the unit test for the Counter class
public class CounterTest extends junit.framework.TestCase {
//Default constructor
public CounterTest() { } 
// Test fixture creates and initializes instance variables, etc.
protected void setUp()
// Releases any system resources used by the test fixture
protected void tearDown()
// These methods contain tests for the Counter methods increment(), decrement(), etc. 
public void testIncrement(); 
public void testDecrement(); 
// Note capitalization convention

JUnit tests for Counter

Note that each test begins with a brand new counter. This means you don’t have to worry about the order in which the tests are run.

public class CounterTest extends junit.framework.TestCase {    Counter counter1;
	// default constructor
	public CounterTest() { }   
	    protected void setUp() {
	    	// creates a (simple) test fixture        	counter1 = new Counter();    }
    public void testIncrement() {        assertTrue(counter1.increment() == 1);        assertTrue(counter1.increment() == 2);     }
    public void testDecrement() {        assertTrue(counter1.decrement() == -1);    }}  

TestSuites

  • TestSuites collect a selection of tests to run them as a unit
  • Collections automatically use TestSuites, however to specify the order in which tests are run, write your own:
	public static Test suite() {
			suite.addTest(new TestBowl(testBowl));
			suite.addTest(new TestBowl(testAdding));
			return suite;
		}
  • Should seldom have to write your own TestSuites as each method in your TestCase should be independent of all others
  • Can create TestSuites that test a whole package:
		public static Test suite() {
			TestSuite suite = new TestSuite();
			suite.addTestSuite(TestBowl.class);
			suite.addTestSuite(TestFruit.class);
			return suite;
		}

JUnit in Eclipse

To create a test class, select File -> New -> Other… -> Java, JUnit, TestCase and enter the name of the class you will test.

Running JUnit

Results

Unit testing for other languages

  • Unit testing tools differentiate between:
    • Errors (unanticipated problems caught by exceptions)
    • Failures (anticipated problems checked with assertions)
  • Basic unit of testing:
    • CPPUNIT_ASSERT(Bool) examines an expression
  • CPPUnit has variety of test classes (e.g. TestFixture)
    • Inherit from them and overload methods

References

[1] JUnit, A programmer-oriented testing framework for Java
[2] C++ port of Junit
[3] Information on Test-Driven Development


comments powered by Disqus