Recapitulation
Unit testing follows a pattern
Lots of small, independent tests
Reporting
Some optional shared setup and teardown
Aggregation (combine tests into test suites)
When you see a pattern, build a framework
Write shared code once
Make it easy for people to do things the right way
JUnit and Its Children
JUnit testing framework written by Erich Gamma in 1997
Now hosted at www.junit.org
Has become the unofficial standard for Java testing
Supported by many IDEs
Widely imitated: C++, Perl, Python, .NET, etc.
Once you know one, you can easily use others
Organization
Using JUnit Directly
Derive a class containing tests from junit.framework.TestCase
Each independent test is a method
Construct instances of the test class
Identify which test each one is to run by passing method name to constructor
Add instances to a suite
Usually an instance of TestSuite
Pass the suite to TestRunner.run()
Checking Results
TestCase has methods to check test results
assertEquals(a, b)
assertNotSame(a, b)
Etc.
Allows JUnit to distinguish expected from unexpected behavior
TestRunner only reports things that require attention
Remember, the more you print, the less people read
Direct Example
public class TestDirect extends TestCase {
public static void main(String[] args) {
TestSuite suite = new TestSuite();
suite.addTest(new TestDirect("testIdentical"));
suite.addTest(new TestDirect("testDouble"));
...etc...
TestRunner.run(suite);
}
public TestDirect(String name) {
super(name);
}
public void testIdentical() {
Rect left = new Rect(0, 0, 1, 1);
Rect right = new Rect(0, 0, 1, 1);
Rect expect = new Rect(0, 0, 1, 1);
assertEquals(left.overlap(right), expect);
}
public void testDouble() {
Rect left = new Rect(0, 0, 1, 1);
Rect right = new Rect(0, 0, 2, 2);
Rect expect = new Rect(0, 0, 1, 1);
assertEquals(left.overlap(right), expect);
}
...etc...
}
Direct Output
Use a slightly broken Rect class
java ...flags... TestDirect ..F..... Time: 0.02 There was 1 failure: 1) testDouble(TestDirect)junit.framework.AssertionFailedError: expected:<null> but was:<<0,0,1,1>> at TestDirect.testDouble(TestDirect.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at TestDirect.main(TestDirect.java:73) FAILURES!!! Tests run: 7, Failures: 1, Errors: 0
Saving Your Wrists
JUnit can use reflection to look up methods given their names
Covered in a later lecture
Pass in the class of your tests
JUnit will decide which methods to run based on their names
If the method name starts with "test", TestRunner will execute it
Remember: programs are data!
Reflection Example
public class TestReflect extends TestCase {
public static void main(String[] args) {
TestSuite suite = new TestSuite(TestReflect.class);
TestRunner.run(suite);
}
...constructor as before...
...test methods as before...
}
Visualizing Tests
JUnit comes with a graphical interface
Give it the name of the test class to run
The origin of the phrase "getting a green bar"
$ java ...options... junit.swingui.TestRunner TestReflect
Testing for Exceptions
Suppose a method is supposed to throw an exception
For example, throw IllegalArgumentException for null parameter
Pattern is:
Call method
Call TestCase.fail() on the next line
Catch specific exception (and do nothing)
(Optional) catch other exceptions, and fail again
Method to test:
public boolean contains(Rect other) throws IllegalArgumentException {
if (other == null) {
throw new IllegalArgumentException("null argument");
}
return (fLoX <= other.fLoX) && (fLoY <= other.fLoY)
&& (fHiX >= other.fHiX) && (fHiY >= other.fHiY);
}
Test code:
public void testThrowForNull() {
Rect a = new Rect(0, 0, 1, 1);
try {
boolean b = a.contains(null);
fail();
}
catch (IllegalArgumentException e) {
// success
}
catch (Exception e) {
fail();
}
}
Setup and Teardown
Sometimes want to run many tests on the same fixture
JUnit supports this:
Calls setUp() before each test
Call tearDown() after each test
Default implementations do nothing
But you can override them...
Remember: every test must be independent
General Rules for Unit Tests
Keep tests small
Each tests exactly one thing
Make tests independent
No side effects
No order dependencies
Don't hard-code things like filenames
Want to be able to move tests around
Use meaningful names
Check results with assertion methods, not raw assert statements
$Id: junit.html,v 1.1.1.1 2004/01/04 05:02:31 reid Exp $