junit training chris yeung 8 th sept, 2006. introduction junit is a regression testing...

35
Junit Training Chris Yeung 8 th Sept, 2006

Upload: clara-wade

Post on 26-Dec-2015

219 views

Category:

Documents


2 download

TRANSCRIPT

Junit Training

Chris Yeung

8th Sept, 2006

Introduction• JUnit is a regression testing frameworkJUnit is a regression testing framework

• Written by Erich Gamma and Kent Written by Erich Gamma and Kent Beck.Beck.

• Used by developers to implement unit Used by developers to implement unit tests in Javatests in Java

• Goal: Accelerate programming and Goal: Accelerate programming and increase the quality of code.increase the quality of code.

• Part of XUnit family (HTTPUnit, Cactus), Part of XUnit family (HTTPUnit, Cactus), CppUnitCppUnit

Why test? Why Junit?• Automated tests prove featuresAutomated tests prove features

• Tests retain their value over time Tests retain their value over time and allows others to prove the and allows others to prove the software still works (as tested). software still works (as tested).

• Confidence, quality, sleepConfidence, quality, sleep

• Effective, open source, integratedEffective, open source, integrated

• Get to code sooner by writing tests.Get to code sooner by writing tests.

What is Junit?• Test framework provides tools for:Test framework provides tools for:

– assertionsassertions

– running testsrunning tests

– aggregating tests (suites)aggregating tests (suites)

– reporting resultsreporting results

• Philosophy always the same:Philosophy always the same:– Let developers write tests.Let developers write tests.

– Make it easy and painless.Make it easy and painless.

– Test early and test oftenTest early and test often

Test infected• It’s a Good Thing, no penicillin neededIt’s a Good Thing, no penicillin needed

• Immediate gratification with build iterationsImmediate gratification with build iterations– Start with “The Simplest Thing That Could Start with “The Simplest Thing That Could

Possibly Work”.Possibly Work”.

– Iterate by successive application of design Iterate by successive application of design pattern.pattern.

• Break the cycle of more pressure == fewer Break the cycle of more pressure == fewer teststests

• Reduce code captivityReduce code captivity– If others can test it, others can work on it.If others can test it, others can work on it.

Junit Mechanics

• Define a subclass of Define a subclass of TestCase.TestCase.

• Override the Override the setUp() & tearDown()setUp() & tearDown()methods.methods.

• Define one or more public Define one or more public testXXX()testXXX()methods methods – Exercise the object(s) under test.Exercise the object(s) under test.

– Asserts the expected results.Asserts the expected results.

• Define a static Define a static suite()suite() factory methodfactory method– Create a TestSuite containing all the tests.Create a TestSuite containing all the tests.

• Optionally define Optionally define main()main() toto run the TestCase run the TestCase in batch modein batch mode..

Junit Mechanics

Simple Testcasepublic class StringTest extends TestCase {public class StringTest extends TestCase { protected void setUp(){ /* run before */}protected void setUp(){ /* run before */}

protected void tearDown(){ /* after */ }protected void tearDown(){ /* after */ }

public void testSimpleAdd() {public void testSimpleAdd() { String s1 = new String(“abcd”); String s1 = new String(“abcd”); String s2 = new String(“abcd”); String s2 = new String(“abcd”); assertTrue(“Strings not equal”,assertTrue(“Strings not equal”, s1.equals(s2)); s1.equals(s2)); }}

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

}}

Simple Testcase (cont.)public static Test suite (){public static Test suite (){ suite = new TestSuite (”StringTest");suite = new TestSuite (”StringTest"); String tests = System.getProperty("tests");String tests = System.getProperty("tests"); if (tests == null){if (tests == null){ suite.addTest(newsuite.addTest(new

TestSuite(StringTest.class));TestSuite(StringTest.class)); }else{}else{ StringTokenizer tokens = newStringTokenizer tokens = new

StringTokenizer(tests, ",");StringTokenizer(tests, ","); while (tokens.hasMoreTokens()){while (tokens.hasMoreTokens()){ suite.addTest(newsuite.addTest(new StringTest((String)tokens.nextToken()));StringTest((String)tokens.nextToken())); }} }} return suite;return suite;}}

<JUnit Report><JUnit Report>

Other assertion methodsOther assertion methods• assertEquals(assertEquals(expectedexpected, , actualactual))

assertEquals(String assertEquals(String messagemessage, , expectedexpected, , actualactual))– This method is heavily overloaded: This method is heavily overloaded: arg1arg1 and and arg2arg2 must must

be both objects be both objects oror both of the same primitive type both of the same primitive type– For objects, uses your equals method, For objects, uses your equals method, if if you have you have

defined it properly, as defined it properly, as public boolean equals(Object public boolean equals(Object o)o)--otherwise it uses --otherwise it uses ====

• assertSame(Object assertSame(Object expectedexpected, Object , Object actualactual))assertSame(String assertSame(String messagemessage, Object , Object expectedexpected, , Object Object actualactual)) – Asserts that two objects refer to the same object (using Asserts that two objects refer to the same object (using

====))• assertNotSame(Object assertNotSame(Object expectedexpected, Object , Object actualactual))

assertNotSame(String assertNotSame(String messagemessage, Object , Object expectedexpected, , Object Object actualactual)) – Asserts that two objects do not refer to the same object Asserts that two objects do not refer to the same object

Other assertion methodsOther assertion methods

• assertNull(Object assertNull(Object objectobject))assertNull(String assertNull(String messagemessage, Object , Object objectobject)) – Asserts that the object is nullAsserts that the object is null

• assertNotNull(Object assertNotNull(Object objectobject))assertNotNull(String assertNotNull(String messagemessage, Object , Object objectobject)) – Asserts that the object is nullAsserts that the object is null

• fail()fail()fail(String fail(String messagemessage)) – Causes the test to fail and throw an Causes the test to fail and throw an

AssertionFailedErrorAssertionFailedError– Useful as a result of a complex test, when the other Useful as a result of a complex test, when the other

assert methods aren’t quite what you wantassert methods aren’t quite what you want

What should I test?

• Tests things which could breakTests things which could break

• Tests should succeed quietly.Tests should succeed quietly.– Don’t print “Doing foo…done with foo!”Don’t print “Doing foo…done with foo!”

– Negative tests, exceptions and errorsNegative tests, exceptions and errors

• What shouldn’t I testWhat shouldn’t I test– Don’t test set/get methodsDon’t test set/get methods

– Don’t test the compilerDon’t test the compiler

Fixtures

• Handle common objects under test Handle common objects under test

• setup() and tearDown() used to setup() and tearDown() used to initialize and release common objects.initialize and release common objects.

• Used to insure there are no side effects Used to insure there are no side effects between tests.between tests.

• Enforce the test independence rule, Enforce the test independence rule, test execution order is not guarunteed.test execution order is not guarunteed.

ExecriseExecrise

• Write a testcase to test 3 method of Write a testcase to test 3 method of java.util.ArrayListjava.util.ArrayList

Test Suitespublic static void main (String [] args){public static void main (String [] args){ junit.textui.TestRunner.run (suite ());junit.textui.TestRunner.run (suite ()); }}

public static Test suite (){public static Test suite (){suite = new TestSuite ("AllTests");suite = new TestSuite ("AllTests");suite.addTest suite.addTest (new TestSuite (AllTests.class));(new TestSuite (AllTests.class));suite.addTest (StringTest.suite());suite.addTest (StringTest.suite());

public void testAllTests () throws Exception{public void testAllTests () throws Exception{ assertTrue (suite != null);assertTrue (suite != null);

}}}}

TestRunners

• TextText– Lightweight, quick quietLightweight, quick quiet

– Run from command lineRun from command linejava StringTestjava StringTest

....... .......

Time: 0.05Time: 0.05

Tests run: 7, Failures: 0, Errors: 0Tests run: 7, Failures: 0, Errors: 0

TestRunners - SwingTestRunners - Swing• Run with java Run with java

junit.swingui.TestRunnerjunit.swingui.TestRunner

Test Runners - EclipseTest Runners - Eclipse

Automating testing (Ant)

• Junit Task <target name="test" depends="compile-tests"><target name="test" depends="compile-tests"> <junit printsummary="yes" fork="yes"><junit printsummary="yes" fork="yes"> <classpath><classpath> <pathelement location="${build}" /><pathelement location="${build}" /> <pathelement location="${build}/test" /><pathelement location="${build}/test" /> </classpath></classpath> <formatter usefile="yes" type="plain" /><formatter usefile="yes" type="plain" /> <test name="AllTests" /><test name="AllTests" /> </junit></junit> </target></target>

Ant Batch mode <target name="batchtest" depends="compile-tests"><target name="batchtest" depends="compile-tests"> <junit printsummary="yes" fork="yes" haltonfailure="no"><junit printsummary="yes" fork="yes" haltonfailure="no"> <classpath><classpath> <pathelement location="${build.dir}" /><pathelement location="${build.dir}" /> <pathelement location="${build.dir}/test" /><pathelement location="${build.dir}/test" /> </classpath></classpath>

<formatter type="plain" usefile="yes"/><formatter type="plain" usefile="yes"/>

<batchtest fork="yes" todir=""><batchtest fork="yes" todir=""> <fileset dir="${test.dir}"><fileset dir="${test.dir}"> <include name="**/*Test.java" /><include name="**/*Test.java" /> </fileset></fileset>

</batchtest></batchtest>

</junit></junit> </target></target>

Designing for testing

– Separation of interface and implementationSeparation of interface and implementation• Allows substitution of implementation to Allows substitution of implementation to

teststests

– Factory patternFactory pattern• Provides for abstraction of creation of Provides for abstraction of creation of

implementations from the tests. implementations from the tests.

– Strategy patternStrategy pattern• Because FactoryFinder dynamically resolves Because FactoryFinder dynamically resolves

desired factory, implementations are desired factory, implementations are plugableplugable

Design for testing - Factories• newnew only used in Factory only used in Factory

• Allows writing tests which can be used Allows writing tests which can be used across multiple implementations.across multiple implementations.

• Promotes frequent testing by writing Promotes frequent testing by writing tests which work against objects tests which work against objects without requiring extensive setupwithout requiring extensive setup

– “ “extra-container” testing.extra-container” testing.

Design for testing - Mock Objects

• When your implementation requires a When your implementation requires a resource which is unavailable for resource which is unavailable for testingtesting

• External system or database is External system or database is simulated.simulated.

• Another use of Factory, the mock Another use of Factory, the mock implementation stubs out and returns implementation stubs out and returns the anticipated results from a request.the anticipated results from a request.

Example of using Mock Example of using Mock ObjectObjectimport 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 Use fixtures to request resource

connection via factory, could be no-connection via factory, could be no-op.op.

• Use vm args or resource bundle to Use vm args or resource bundle to drive which factory is used.drive which factory is used.

• Data initialization/clearing handled by Data initialization/clearing handled by fixtures to preserve order fixtures to preserve order independence of tests.independence of tests.

Develop testcase with Develop testcase with database using abstract database using abstract base classbase 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 testingIn-container unit testing

• There are tools like cactus and There are tools like cactus and StrutsTestCaseStrutsTestCase

• Excellent for testing:Excellent for testing:– EJBEJB– Servlets, Filters, TaglibsServlets, Filters, Taglibs– Container-dependent frameworks, like Container-dependent frameworks, like

StrutsStruts

JUnit Best PracticesJUnit Best Practices• Separate production and test codeSeparate production and test code• But typically in the same packagesBut typically in the same packages• Compile into separate trees, allowing Compile into separate trees, allowing

deployment without testsdeployment without tests• Don’t forget OO techniques, base Don’t forget OO techniques, base

classingclassing• Test-driven developmentTest-driven development

1.1. Write failing test firstWrite failing test first2.2. Testing for ExceptionsTesting for Exceptions3.3. Test then FixTest then Fix4.4. Test then RefactorTest then Refactor5.5. Where should I put my test files?Where should I put my test files?

Write failing test first

• Write your test first, or at least at the Write your test first, or at least at the same timesame time

• Test what can breakTest what can break

• Create new tests to show bugs then Create new tests to show bugs then fix the bugfix the bug

• Test driven development says write Test driven development says write the test then make it pass by coding the test then make it pass by coding to it.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!)Bugs occasionally slip through (gasp!)

• Write a test first which demonstrates Write a test first which demonstrates the error. Obviously, this test is the error. Obviously, this test is needed.needed.

• Now, fix the bug and watch the bar go Now, fix the bug and watch the bar go green!green!

• Your tests assure the bug won’t Your tests assure the bug won’t reappear.reappear.

Test then RefactorTest then Refactor

• Once the code is written you want to Once the code is written you want to improve it.improve it.

• Changes for performance, Changes for performance, maintainability, readability.maintainability, readability.

• Tests help you make sure you don’t Tests help you make sure you don’t break it while improving it.break it while improving it.

• Small change, test, small change, Small change, test, small change, test...test...

Where should I put my test Where should I put my test files?files?

You can place your tests in the same package and directory as the You can place your tests in the same package and directory as the classes under test. For example: classes under test. For example: srcsrc comcom xyzxyz SomeClass.javaSomeClass.java SomeClassTest.javaSomeClassTest.java An arguably better way is to place the tests in a separate parallel An arguably better way is to place the tests in a separate parallel directory structure with package alignment. directory structure with package alignment. For example: For example: srcsrc comcom xyzxyz SomeClass.javaSomeClass.javatesttest comcom xyzxyz SomeClassTest.javaSomeClassTest.java

These approaches allow the tests to access to all the public and package These approaches allow the tests to access to all the public and package visible methods of the classes under test.visible methods of the classes under test.

Resources

• http://www.junit.orghttp://www.junit.org

• http://www.xprogramming.comhttp://www.xprogramming.com

• http://www-106.ibm.com/http://www-106.ibm.com/developerworks/java/library/j-developerworks/java/library/j-junitmail/index.htmljunitmail/index.html

• http://jakarta.apache.org/anthttp://jakarta.apache.org/ant