JUNIT presentation

Download Report

Transcript JUNIT presentation

Junit Training
Chris Yeung
8th Sept, 2006
Introduction
• JUnit is a regression testing framework
• Written by Erich Gamma and Kent Beck.
• Used by developers to implement unit
tests in Java
• Goal: Accelerate programming and
increase the quality of code.
• Part of XUnit family (HTTPUnit, Cactus),
CppUnit
Why test? Why Junit?
• Automated tests prove features
• Tests retain their value over time and
allows others to prove the software still
works (as tested).
• Confidence, quality, sleep
• Effective, open source, integrated
• Get to code sooner by writing tests.
What is Junit?
• Test framework provides tools for:
– assertions
– running tests
– aggregating tests (suites)
– reporting results
• Philosophy always the same:
– Let developers write tests.
– Make it easy and painless.
– Test early and test often
Test infected
• It’s a Good Thing, no penicillin needed
• Immediate gratification with build iterations
– Start with “The Simplest Thing That Could Possibly
Work”.
– Iterate by successive application of design pattern.
• Break the cycle of more pressure == fewer
tests
• Reduce code captivity
– If others can test it, others can work on it.
Junit Mechanics
• Define a subclass of TestCase.
• Override the setUp() & tearDown()methods.
• Define one or more public testXXX()methods
– Exercise the object(s) under test.
– Asserts the expected results.
• Define a static suite() factory method
– Create a TestSuite containing all the tests.
• Optionally define main() to run the TestCase in
batch mode.
Junit Mechanics
Simple Testcase
public class StringTest extends TestCase {
protected void setUp(){ /* run before */}
protected void tearDown(){ /* after */ }
public void testSimpleAdd() {
String s1 = new String(“abcd”);
String s2 = new String(“abcd”);
assertTrue(“Strings not equal”,
s1.equals(s2));
}
public static void main(String[] args){
junit.textui.TestRunner.run (suite ());
}
}
Simple Testcase (cont.)
public static Test suite (){
suite = new TestSuite (”StringTest");
String tests = System.getProperty("tests");
if (tests == null){
suite.addTest(new
TestSuite(StringTest.class));
}else{
StringTokenizer tokens = new
StringTokenizer(tests, ",");
while (tokens.hasMoreTokens()){
suite.addTest(new
StringTest((String)tokens.nextToken()));
}
}
return suite;
}
<JUnit Report>
Other assertion methods
• assertEquals(expected, actual)
assertEquals(String message, expected, actual)
– This method is heavily overloaded: arg1 and arg2 must be
both objects or both of the same primitive type
– For objects, uses your equals method, if you have defined it
properly, as public boolean equals(Object o)--otherwise it
uses ==
• assertSame(Object expected, Object actual)
assertSame(String message, Object expected,
Object actual)
– Asserts that two objects refer to the same object (using ==)
• assertNotSame(Object expected, Object actual)
assertNotSame(String message, Object expected,
Object actual)
– Asserts that two objects do not refer to the same object
Other assertion methods
• assertNull(Object object)
assertNull(String message, Object object)
– Asserts that the object is null
• assertNotNull(Object object)
assertNotNull(String message, Object object)
– Asserts that the object is null
• fail()
fail(String message)
– Causes the test to fail and throw an AssertionFailedError
– Useful as a result of a complex test, when the other assert
methods aren’t quite what you want
What should I test?
• Tests things which could break
• Tests should succeed quietly.
– Don’t print “Doing foo…done with foo!”
– Negative tests, exceptions and errors
• What shouldn’t I test
– Don’t test set/get methods
– Don’t test the compiler
Fixtures
• Handle common objects under test
• setup() and tearDown() used to initialize
and release common objects.
• Used to insure there are no side effects
between tests.
• Enforce the test independence rule, test
execution order is not guarunteed.
Execrise
• Write a testcase to test 3 method of
java.util.ArrayList
Test Suites
public static void main (String [] args){
junit.textui.TestRunner.run (suite ());
}
public static Test suite (){
suite = new TestSuite ("AllTests");
suite.addTest
(new TestSuite (AllTests.class));
suite.addTest (StringTest.suite());
public void testAllTests () throws Exception{
assertTrue (suite != null);
}
}
TestRunners
• Text
– Lightweight, quick quiet
– Run from command line
java StringTest
.......
Time: 0.05
Tests run: 7,
Failures: 0,
Errors: 0
TestRunners - Swing
• Run with java junit.swingui.TestRunner
Test Runners - Eclipse
Automating testing (Ant)
• Junit Task
<target name="test" depends="compile-tests">
<junit printsummary="yes" fork="yes">
<classpath>
<pathelement location="${build}" />
<pathelement location="${build}/test" />
</classpath>
<formatter usefile="yes" type="plain" />
<test name="AllTests" />
</junit>
</target>
Ant Batch mode
<target name="batchtest" depends="compile-tests">
<junit printsummary="yes" fork="yes" haltonfailure="no">
<classpath>
<pathelement location="${build.dir}" />
<pathelement location="${build.dir}/test" />
</classpath>
<formatter type="plain" usefile="yes"/>
<batchtest fork="yes" todir="">
<fileset dir="${test.dir}">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>
</target>
Designing for testing
– Separation of interface and implementation
• Allows substitution of implementation to tests
– Factory pattern
• Provides for abstraction of creation of
implementations from the tests.
– Strategy pattern
• Because FactoryFinder dynamically resolves
desired factory, implementations are plugable
Design for testing - Factories
• new only used in Factory
• Allows writing tests which can be used
across multiple implementations.
• Promotes frequent testing by writing
tests which work against objects without
requiring extensive setup
– “extra-container” testing.
Design for testing - Mock Objects
• When your implementation requires a
resource which is unavailable for testing
• External system or database is simulated.
• Another use of Factory, the mock
implementation stubs out and returns the
anticipated results from a request.
Example of using Mock Object
import org.jmock.*;
class PublisherTest extends MockObjectTestCase {
public void testOneSubscriberReceivesAMessage() {
// set up, subscriber can be any class
Mock mockSubscriber = mock(Subscriber.class);
Publisher publisher = new Publisher();
publisher.add((Subscriber) mockSubscriber.proxy());
final String message = "message";
// expectations
mockSubscriber.expects(once()).method("receive").with(
eq(message) );
// execute
publisher.publish(message);
}
}
•Of course, you can write mock yourself by
implement interface with simple implementation
Testing with resources
(EJB/DB)
• Use fixtures to request resource
connection via factory, could be no-op.
• Use vm args or resource bundle to drive
which factory is used.
• Data initialization/clearing handled by
fixtures to preserve order independence
of tests.
Develop testcase with
database using abstract base
class
public abstract class DatabaseTestCase extends TestCase{
protected final void setUp() throws SQLException, IOException {
resetData();
DefaultDataManager.setupDefaultData();
databaseSetUp();
}
protected final void tearDown() throws SQLException {
this.databaseTearDown();
this.getConnection().close();
}
protected void databaseSetUp() throws SQLException, IOException {
}
protected void databaseTearDown() throws SQLException {
}
public final Connection getConnection() {
return currentContext.connection;
}
}
In-container unit testing
• There are tools like cactus and
StrutsTestCase
• Excellent for testing:
– EJB
– Servlets, Filters, Taglibs
– Container-dependent frameworks, like Struts
JUnit Best Practices
•
•
•
•
•
Separate production and test code
But typically in the same packages
Compile into separate trees, allowing
deployment without tests
Don’t forget OO techniques, base classing
Test-driven development
1.
2.
3.
4.
5.
Write failing test first
Testing for Exceptions
Test then Fix
Test then Refactor
Where should I put my test files?
Write failing test first
• Write your test first, or at least at the
same time
• Test what can break
• Create new tests to show bugs then fix
the bug
• Test driven development says write the
test then make it pass by coding to it.
Testing for Exceptions
public void testExpectException()
{
String s1 = null;
String s2 = new String("abcd");
try{
s1.toString();
fail("Should see null pointer");
}
catch(NullPointerException ex){
}
}
Test then Fix
• Bugs occasionally slip through (gasp!)
• Write a test first which demonstrates the
error. Obviously, this test is needed.
• Now, fix the bug and watch the bar go
green!
• Your tests assure the bug won’t reappear.
Test then Refactor
• Once the code is written you want to
improve it.
• Changes for performance, maintainability,
readability.
• Tests help you make sure you don’t break
it while improving it.
• Small change, test, small change, test...
Where should I put my test
files?
You can place your tests in the same package and directory as the classes
under test. For example:
src
com
xyz
SomeClass.java
SomeClassTest.java
An arguably better way is to place the tests in a separate parallel directory
structure with package alignment.
For example:
src
com
xyz
SomeClass.java
test
com
xyz
SomeClassTest.java
These approaches allow the tests to access to all the public and package visible
methods of the classes under test.
Resources
• http://www.junit.org
• http://www.xprogramming.com
• http://www106.ibm.com/developerworks/java/library
/j-junitmail/index.html
• http://jakarta.apache.org/ant