Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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