Junit - Getting started with an example | Last update : 09/12/2002 | back to HOME PAGE |
|
Introduction |
The goal of this article is getting started with Junit starting from a simple example.
Unit tests, integration tests and no regression tests take a large part in developer's life. Junit is an open source test framework which is using by java developer's community.
Contents :
Why using a test framework ?
This chapter contains some extracts from article Programmers Love Writing Tests.
Every programmer knows they should write tests for their code. Few do.
The universal response to "Why not?" is "I'm in too much of a hurry." This quickly becomes a vicious cycle- the more pressure you feel, the fewer tests you write. The fewer tests you write, the less productive you are and the less stable your code becomes. The less productive and accurate you are, the more pressure you feel.
Using a test framework is asking a remarkably small investment and will make you a faster, more productive, more predictable, and less stressed developer.
Here are a couple of the times that you will receive a reasonable return on your testing investment:
Then debug until the test succeeds. You will be able to refactor much more aggressively once you have the tests. You won't understand at first just how much you can do, though. Try to catch yourself saying, "Oh, I see, I should have designed this thus and so. I can't change it now. I don't want to break anything." When you say this, save a copy of your current code and give yourself a couple of hours to clean up. Make your changes, all the while running your tests.
You will be surprised at how much ground you can cover in a couple of hours if you aren't worrying every second about what you might be breaking.
Another very good article introducing Junit is here : Junit Primer by Dave Clark.
1. A simple example |
Here is a simple class with public methods we want to test using Junit :
- getHelloWorld, return a String
- getTruth, return a boolean
- getElement and setElement, which throw exceptions
public class Foo { private int[] array; public Foo(int size) { array= new int[size]; } public String getHelloWorld() { return "Hello World"; } public boolean getTruth() { return true; } public void setElement(int position, int value) throws ArrayIndexOutOfBoundsException { array[position] = value; } public int getElement(int position) throws ArrayIndexOutOfBoundsException { return array[position]; } }How do you used to write testing code?
The simplest way is as an expression in a debugger. You can change debug expressions without recompiling, and you can wait to decide what to write until you have seen the running objects. You can also write test expressions as statements which print to the standard output stream. Both styles of tests are limited because they require human judgment to analyze their results. Also, they don't compose nicely- you can only execute one debug expression at a time and a program with too many print statements causes the dreaded "Scroll Blindness".
Using Junit, you won't "pollute" the source code because test code is separated from Business classes.
JUnit tests do not require human judgment to interpret, and it is easy to run many of them at the same time. When you need to test something, you create an instance of TestCase .
2 TestCase |
import junit.framework.*; import junit.textui.*; public class TestFoo extends TestCase { // Foo instance used for tests protected Foo Foo; protected int testSize; // standard constructor public TestFoo(String name) { super(name); } /** * This method is called every time before particular test execution. * It creates new instance of tested class and it can perform some more * actions which are necessary for tests. */ protected void setUp(){ testSize = 3; Foo = new Foo(testSize); } /** * test original method getHelloWorld, example of Assert.assertEquals */ public void testGetHelloWorld() { System.out.println("testGetHelloWorld"); /* This test code makes use of assertEquals - one of a number of assertion methods provided by the JUnit interface Assert. There are overloaded variants of assertEquals to deal with various types of parameter, including the base types, containers, and Strings - if the two parameters are equals then it simply returns, otherwise it throws an unchecked exception used within the framework to report on the error. */ // as the method returns "Hello World", the test should fail String result = "Hello world"; Assert.assertEquals(result, Foo.getHelloWorld()); } /** * test original method getTruth, example of Assert.assertTrue */ public void testGetTruth() { System.out.println("testGetTruth"); /* assertTrue checks if its parameter equals true, otherwise it throws an unchecked exception used within the framework to report on the error */ Assert.assertTrue(Foo.getTruth()); } /** * test original method setElement, example of Assert.fail */ public void testSetElement() { /* This test consists in checking that an exception is throwed in method getException trying to access element at position "size" of an array should throw an ArrayIndexOutOfBoundsException. */ try { System.out.print("testSetElement, exception throwed : --> "); Foo.setElement(testSize, 0); /* if exception is not catched, line Assert.fail is executed : test fails */ Assert.fail("setElement should raise an Exception"); } catch (Exception e) { System.out.println(e.toString()); } // test with valid parameter (no exception supposed to be throwed) try { System.out.println("testSetElement, OK "); int position = testSize - 1; int result = 10; Foo.setElement(position, result); Assert.assertEquals(result, Foo.getElement(position)); } catch (Exception e) { System.out.println(e.toString()); } } /** * test original method getElement : to do ! */ public void testGetElement() { } /** * Once you have several tests, use a Suite to organize them. * this method returns all tests which should be performed for testing class. * By default it returns only name of testing class. Instance of this * is then created with its constructor. */ public static Test suite() { /* Junit uses reflexion to add automatically all the methods of TestFoo whose name begins by "test" */ return new TestSuite(TestFoo.class); } /** * This main method is used for run tests for this class only * from command line. */ public static void main(String[] args) { /* to use command line interface */ junit.textui.TestRunner.run(suite()); // to use GUI interface (no Suite paramater permitted) //junit.swingui.TestRunner.run(TestFoo.class); } // end of main(Stringp[] args) }back to Top
3 Test results |
.testGetHelloWorld F.testGetTruth .testSetElement, exception throwed : --> java.lang.ArrayIndexOutOfBoundsException testSetElement, OK . Time: 0,015 There was 1 failure: 1) testGetHelloWorld(TestFoo) junit.framework.AssertionFailedError: expected:<Hello world> but was:<Hello World>at TestFoo.testGetHelloWorld(TestFoo.java:42) at TestFoo.main(TestFoo.java:107) FAILURES!!! Tests run: 4, Failures: 1, Errors: 0
GUI Inteface Result :
4 TestCase generator : unittestsgen by Artur Hefczyc |
unittestsgen is a Test class code generator developed by Artur
Hefczyc. It implements a task which allows use the generator from ANT
and control test classes generating from build.xml file. You can download it
at this url : http://sourceforge.net/projects/wttools/
How to use unittestgen
In MS Windows, the command line looks like this : D:\projects\yourproject> java -jar unittestsgen.jar -cp "yourjarfile.jar;junit.jar"
If you work with a war, you can download batches here.
Extract of FooTestcase generated by unittestgen :
/** * Fileback to TopFooTestCase.java
is automaticaly generated by * 'unittestsgen' application. Code generator is created for java * sources and for 'junit' package by "Artur Hefczyc" * kobit@users.sourceforge.net
*/ public class FooTestCase extends TestCase { /** * Instance of tested class. */ protected Foo varFoo; /** * Public constructor for creating testing class. */ public FooTestCase(String name) { super(name); } // end of FooTestCase(String name) /** * This main method is used for run tests for this class only * from command line. */ public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } // end of main(Stringp[] args) /** * This method is called every time before particular test execution. * It creates new instance of tested class and it can perform some more * actions which are necessary for performs tests. */ protected void setUp() { // Initialize your variable(s) here // for example: // varFoo = new Foo(...); // But note that there is no default constructor in Foo } // end of setUp() /** * Returns all tests which should be performed for testing class. * By default it returns only name of testing class. Instance of this * is then created with its constructor. */ public static Test suite() { return new TestSuite(FooTestCase.class); } // end of suite() /** * for classes which doesn't contain any methods here is one additional * method for performing test on such classes. */ public void testNoMethods() { } /** * Method for testing how works original method: * int getElement(int) * from tested class */ public void testGetElement104431() { /* No default constructor, code commented out... // Hint: in get/set pair 'get' method should _not_ have any parameters // and should return int as a value. // Warning in get/set methods pair 'set' method should have only one // parameter. The same type which is returned by corresponding get method. */ } // end of testGetElement104431(int) } // end of FooTestCase Using ant with unittestgen, build.xml script <project name="UnitTestsGen" default="dist" basedir=".">
<taskdef name="unitgen" classname="testsgen.taskant.UnitTestsGen"/>
<property name="src" value="src"/>
<property name="test-src" value="test-src"/>
<property name="build" value="build"/>
<target name="compile" description="Compile sources in ${tmpsrc} directory">
<mkdir dir="${build}"/>
<javac srcdir="${src};${test-src}" destdir="${build}"
debug="on" deprecation="off">
<classpath>
<pathelement location="${libs}/junit.jar"/>
</classpath>
</javac>
</target>
<target name="jar" depends="compile">
<jar jarfile="jar/${jarfile}.jar" manifest="MANIFEST.MF" basedir="${build}">
<exclude name="** /*TestCase*.*"/>
<exclude name="TestAll.*"/>
</jar>
<unitgen update="true" classpath="${build};${libs}/junit.jar"
imput="${src}" output="${test-src}"/>
</target>
</project>
5 Links |
Junit Home site, billions of links : http://www.junit.org/
Download the TestCase generator tool at http://sourceforge.net/projects/wttools/
Junit discussion group : http://groups.yahoo.com/group/junit/
Another article introducing Junit : Junit Primer
Jbuilder6 integrates Junit, an Javaworld article about what's new in JBuilder6
:
http://www.javaworld.com/javaworld/jw-01-2002/jw-0118-iw-jbuilder_p.html
IBM presents its Application Quality Assurance :
http://www7b.boulder.ibm.com/wsdd/library/presents/junit.html (contains pdf)
6 Download |
Download the examples and some batch files (for windows) to use unitestgen with a war.
Credits |
Author
I am an European software engineer who lives and works in London. For any comments / suggestions : send an e-mail to g a r n i e r j m @ y a h o o.f r (remove the spaces to get my e-mail, I have done that bc the sobig virus has filled my mailbox!)
Copyrights
The source is free to use and is available under the GNU GENERAL PUBLIC LICENSE.
|