1
Writing Tests in JUnit (vers. 3.8)
Filippo Ricca DISI, Università di Genova, Italy
2
Agenda
JUnit (v. 3.8, now: 4.8.1) Assert*() TestSuite Testing “Exceptions” SetUp() and tearDown()
JUnit in eclipse Test-last vs.Test-first Test-first example
current account Integration and System Testing in Junit
A little … Mock objects Junit 3.8 vs. Junit 4.X
3
Testing with JUnit
Junit is a unit test environment for Java programs
Writing test cases in Java Executing test cases
Pass/fail? (red/green bar)
It is developed by Eric Gamma and Kent Beck It consists of a framework providing all the
tools for testing framework: set of Java classes and conventions to
use them It is integrated into eclipse through a
graphical plug-in
4
Junit
Test framework: test cases are “Java code” test case = “sequence of operations + inputs + expected values”
Test code testDoubleOf(){ //
… }
Production code int doubleOf(){ //
… }
assertion
5
Using main() … JUnit tests “substitute the use of main() to check the
program behaviour” public class Stack { … public static void main (String[] args) {� Stack aStack = new Stack();� if (!aStack.isEmpty()) {� System.out.println (“Stack should be empty!”);
} aStack.push(10);�
aStack.push(-4);� System.out.println(“Last element:“ + aStack.pop());� System.out.println(“First element: “ + aStack.pop());
} }
-4
10
isempty() pop() push(…)
6
Framework elements
Package junit.framework: It provides Junit core classes
1. TestCase “Base class” for classes that contain tests Our test classes will extend it
2. Assert Assert*() = Methods family to check
conditions 3. TestSuite
Enables grouping several test cases
7
JUnit to run programs
All we need to do is: 1. Write a sub-class of
TestCase 2. Add to it one or
more test methods Assert*()
3. Run the test using JUnit
8
Test Class
import junit.framework.TestCase; public class StackTester extends TestCase {
public StackTester(String name) { super(name);
} public void testStack() {
Stack aStack = new Stack(); if (!aStack.isEmpty()) { System.out.println(“Stack should be empty!”); } aStack.push(10); aStack.push(-4); assertEquals(-4, aStack.pop());
assertEquals(10, aStack.pop()); }
}
Code generated automatically by
Eclipse
Must begin with “test”
Good practice: A “test class” for each class of the project
Noting …
9
Assert Assert*() are public static methods defined in the class Assert You have not to import the class Assert because TestCase extends
Assert Their names begin with “assert” and they are used in test methods
es. assertTrue(“stack should be empty”, aStack.empty());
If the condition is false: 1. test fails (red bar) 2. execution skips the rest of the test method 3. the message (if any) is printed
If the condition is true: execution continues normally
If all the conditions in the test methods are true then the test pass (green bar)
condition
10
Assert*() and fail() for a boolean condition
assertTrue(“message for fail”, boolean condition);
for objects, int, long, boolean, strings, byte … assertEquals(“message for fail”, expected_value, actual);
for “our objects” the appropriate equals method will be used the equals methods for array does not compare the contents just the array
reference itself
for float and double values assertEquals (“message for fail”, expected, actual, error);
Asserts that expected and actual refer to the same object assertSame(“message for fail”, expected_value, actual); assertNotSame(“message for fail”, expected_value, actual);
Fails the test immediately fail (“message for fail” );
“tolerance”
11
Assert: example import junit.framework.TestCase;
public class StackTester extends TestCase {
public StackTester(String name) { super(name);
}
public void testStack() { Stack aStack = new Stack(); assertTrue(“Stack should be empty!”, aStack.isEmpty()); aStack.push(10); assertTrue(“Stack should not be empty!”,!aStack.isEmpty()); aStack.push(-4); assertEquals(-4, aStack.pop()); assertEquals(10, aStack.pop()); } }
12
One concept at a time … public class StackTester extends TestCase {
public void testStackEmpty() { Stack aStack = new Stack(); assertTrue(“Stack should be empty!”, aStack.isEmpty()); aStack.push(10); assertTrue(“Stack should not be empty!”, !aStack.isEmpty());
}
public void testStackOperations() { Stack aStack = new Stack();
aStack.push(10); aStack.push(-4); assertEquals(-4, aStack.pop()); assertEquals(10, aStack.pop());
} }
“Modularization …”
13
Working rule
For each test class: JUnit execute all of its public test methods
i.e. those whose name starts with “test” JUnit ignores everything else …
Test classes can contain “helper methods” provided that are: non public, or whose name does not begin with “test”
14
TestSuite
public class AllTests extends TestSuite {
public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(StackTester.class); suite.addTestSuite(ArrayTester.class);
suite.addTestSuite(SetTester.class); … } }
For example to test a project with more classes …
• Groups several test classes
15
Test of “Exceptions”
16
We expect an exception … try { // we call the method with wrong parameters object.method(null); fail(“method should launch the exception!!"); } catch(PossibleException e){ assertTrue(true); // OK } class TheClass {
public void method(String p) throws PossibleException
{ /*... */ } }
Good practice: test each exception!
“null launch the exception …”
17
We expect a normal behavior …
try { // We call the method with correct parameters object.method(“Parameter"); assertTrue(true); // OK } catch(PossibleException e){ fail (“method should not launch the exception !!!"); } class TheClass {
public void method(String p) throws PossibleException
{ /*... */ } }
18
SetUp and TearDown setUp() method initialize object(s) under test
called before every test method starting point is equal for each test method
tearDown() method release object(s) under test called after every test case method.
ShoppingCart cart; Book book; protected void setUp() {
cart = new ShoppingCart(); book1 = new Book(“JUnit", 29.95); book2 = new Book(“XP", 18.25); cart.addItem(book1); cart.addItem(book2);}
1 setUp() 2 testIsEmpty() 3 tearDown()
4 setUp() 5 testPop() 6 tearDown()
7 setUp() 8 testPush() 9 tearDown()
public class StackTester extends TestCase { testIsEmpty() {...} testPop() {...} testPush(…) {...} }
19
Setup() example public class StackTester extends TestCase {
Stack aStack;
protected void setUp() { aStack = new Stack(); } public void testStackEmpty() {
Stack aStack = new Stack(); assertTrue(“Stack should be empty!”, aStack.isEmpty()); aStack.push(10); assertTrue(“Stack should not be empty!”, !aStack.isEmpty());
} public void testStackOperations() { Stack aStack = new Stack(); aStack.push(10); aStack.push(-4); assertEquals(-4, aStack.pop()); assertEquals(10, aStack.pop()); } }
20
Junit in Eclipse - Setup
Already integrated … Setup:
Select a project Open project’s property window File -> Properties Select: Java build path Select: libraries Add Library Select Junit
If everything is ok ....
21
Run as JUnit Test Run
Run As Junit Test
22
Red / Green Bar
expected <-3> but was <-4>
StackTester 28: assertEquals( -3, aStack.pop() );
actual
expected actual
23
Failures vs. Errors
Failure: assertion not satisfied assertEquals( -3, aStack.pop() );
Error: run time error assertEquals( -3, saldo.getSaldo() );
expected <-3> but was <-4>
java.lang.ArrayIndexOutOfBoundsException …
24
Test last
New functionality Understand
Implement functionality
Write tests
Run all tests
Result? Rework fail
pass
Next functionality
“A new functionality” correspond to a class with methods
25
Test first
New functionality Understand
Add a single test
Add code for the test
Run all test
Result? Rework
Functionality complete?
fail pass No
Next functionality Yes
XP champions the use of tests as a development tool … Stepwise approach …
26
Junit in practice …
New Functionality: Create a class current account (bank account)
deposit (deposito) withdraw (prelievo) settlement (saldo)
Example: CurrentAccount cc = new CurrentAccount(); cc.deposit(12);
cc.draw(-8); cc.deposit(10); cc.settlement()
expected value 14 euro!
27
28
Add Testcases …
class Test_CurrentAccount extends TestCase{
public void test_settlementVoid() { CurrentAccount c = new CurrentAccount(); assertTrue(c.settlement() == 0); }
public void test_settlement() { CurrentAccount c = new CurrentAccount(); c.deposit(12); c.draw(-8); c.deposit(10); assertTrue(c.settlement() == 14); } }
Test first …
“assertion”
29
30
Add the skeleton of the class
class CurrentAccount { int account[]; int lastMove;
CurrentAccount(){ lastMove=0; account=new int[10]; }
public void deposit(int value){ account[lastMove]=value; lastMove++; }
public void draw(int value) { account[lastMove]=value; lastMove++; }
public int settlement() {return 3;}
public static void main(String args[]) {} }
class Test_CurrentAccount extends TestCase{
public void test_settlementVoid() { currentAccount c = new currentAccount(); assertTrue(c.settlement() == 0); }
public void test_settlement() { currentAccount c = new currentAccount(); c.deposit(12); c.draw(-8); c.deposit(10); assertTrue(c.settlement() == 14); } }
31
32
Run Junit (first time)
33
34
Rework class CurrentAccount { int account[]; int lastMove;
CurrentAccount(){ lastMove=0; account=new int[10]; }
public void deposit(int value){ …}
public void draw(int value) { …}
public int settlement() { int result = 0 for (int i=0; i<account.length; i++) { result = result + account[i]; } return result; }
public static void main(String args[]) {} }
class Test_CurrentAccount extends TestCase{
public void test_settlementVoid() { currentAccount c = new currentAccount(); assertTrue(c.settlement() == 0); }
public void test_settlement() { currentAccount c = new currentAccount(); c.deposit(12); c.draw(-8); c.deposit(10); assertTrue(c.settlement() == 14); } }
35
36
Run Junit (second time)
37
38
Add a new “real” testcase class CurrentAccount { int account[]; int lastMove;
CurrentAccount(){ lastMove=0; account=new int[10]; }
public void deposit(int value){ …}
public void draw(int value) { …}
public int settlement() { int result = 0 for (int i=0; i<account.length; i++) { result = result + account[i]; } return result; }
public static void main(String args[]) {} }
class Test_currentAccount extends TestCase{
…
public void test_realCaseSettlement() { currentAccount c = new currentAccount(); for (int i=0; i <10 ; i++) c.deposit(1); c.draw(-10); assertTrue(c.settlement() == 0); } }
39
Run JUnit (third time) Run time error
40
41
class CurrentAccount { int account[]; int lastMove;
CurrentAccount(){ lastMove=0; account=new int[100]; }
public void deposit(int value){ …}
public void draw(int value) { …}
public int settlement() { int result = 0 for (int i=0; i<account.length; i++) { result = result + account[i]; } return result; }
public static void main(String args[]) {} }
class Test_currentAccount extends TestCase {
…
public void test_realCaseSettlement() { currentAccount c = new currentAccount(); for (int i=0; i <10 ; i++) c.deposit(1); c.draw(10); assertTrue(c.settlement() == 0); } }
Rework “Stupid fix”
42
43
Run JUnit (fourth time)
44 “I can refactoring the program without anxiety. I have the testcases …”
45 “changing the data structure: Array --> List”
public class CurrentAccount { List account = new LinkedList();
public void deposit(int value) { account.add(new Integer(value)); }
public void draw(int value) { account.add(new Integer(value)); }
public int settlement() { int result = 0; Iterator it=account.iterator(); while (it.hasNext()) { Integer value_integer = (Integer)it.next(); int val = value_integer.intValue(); result = result + val; } return result; }
}
Refactoring
46
Run JUnit (fifth time)
47
“The end”
48
Test First Advantages Each method has associated a testcase
the confidence of our code increases … It simplifies:
refactoring/restructuring maintenance the introduction of new functionalities
Test first help to build the documentation testcases are good “use samples”
Programming is more fun … “it is a challenge against the testcases”
49
Test First Disadvantages
Writing test cases using the test-first approach is expensive and difficult Effort required seems high … Number of Locs are double …
Software quality is better? Contrasting empirical results
50
Integration and System Testing Junit is a “environment for unit testing” but can be used:
for integration testing Other more specific tools, i.e., JFunc
for system (GUI) testing Other more specific tools, i.e., JFCUnit
JUnit
SetUp()
Assert*()
51
Mock objects (1) Mock objects are simulated objects that “mimic”
the behavior of real objects in controlled ways Same interface
A programmer typically creates a mock object to test the behavior of some other objects …
Reasons for using Mock objects. The real object: supplies non-deterministic results
e.g., the current time or the current temperature is difficult to set up has states that are difficult to create or reproduce
e.g., a network error is slow
e.g., a complete database, which would have to be initialized before the test
does not yet exist or may change behavior common problem when working with other teams
A
SUT
A’ mock
Mock objects (2)
Mock objects are used: In Unit testing to isolate
units from the rest In top down integration
testing
52
A
Unit
A’ main
A B C
D E F
I have to test the main ... mock
mocks
Mock objects example (1) Class under test Interface
Real class used
Mock class
54
Mock objects example (2) • In the production
environment the real class is used
• The test code, on the other hand, uses a MockSystemEnvironment
• The code under test that uses getTime() doesn't know the difference between a test environment and the real environment, as they both implement the same interface
Object to test
16.55
false
17.00
true
19.00
true
55
Junit 3.8 vs. 4.x Junit 4 is:
a different API from Junit 3.8 simpler, richer and easier to use …
Junit 4 is based on Java annotations (@) It introduces at least:
More flexible initialization @Before, @After, @BeforeClass, @AfterClass
Timeout Tests @Test(timeout = 1000)
New asset method used to compare array of objects Both arrays are equal if each element they contain is equal!
Handling of Java exceptions Parameterized test cases @ignore @Test substitutes testMethod()
setUP() testMethod1() testMethod2() testMethod3() tearDown()
56
JUnit 3.8 JUnit 4 packagejunit3;
importcalc.Calculator;importjunit.framework.TestCase;
publicclassCalculatorTestextendsTestCase{privatestaticCalculatorcalculator=newCalculator();
protectedvoidsetUp(){calculator.clear();}
publicvoidtestAdd(){calculator.add(1);calculator.add(1);assertEquals(calculator.getResult(),2);}
publicvoidtestDivide(){calculator.add(8);calculator.divide(2);assertTrue(calculator.getResult()==4);}
publicvoidtestDivideByZero(){try{calculator.divide(0);fail();}catch(ArithmeticExceptione){}}
publicvoidnotReadyYetTestMultiply(){calculator.add(10);calculator.multiply(10);assertEquals(calculator.getResult(),100);}
}
packagejunit4;importcalc.Calculator;importorg.junit.Before;importorg.junit.Ignore;importorg.junit.Test;importstaticorg.junit.Assert.*;
publicclassCalculatorTest{privatestaticCalculatorcalculator=newCalculator();
@BeforepublicvoidclearCalculator(){calculator.clear();}
@Testpublicvoidadd(){calculator.add(1);calculator.add(1);assertEquals(calculator.getResult(),2);}
@Testpublicvoiddivide(){calculator.add(8);calculator.divide(2);assertTrue(calculator.getResult()==4);}
@Test(expected=ArithmeticException.class)publicvoiddivideByZero(){calculator.divide(0);}
@Ignore("notreadyyet")@Testpublicvoidmultiply(){calculator.add(10);calculator.multiply(10);assertEquals(calculator.getResult(),100);}
}
57
Parameterized Test Cases import org.junit.*; import static org.junit.Assert.*;
public class AdditionTest {
@Parameters public static int[][] data = new int[][] { {0, 0, 0}, {1, 1, 0}, {2, 1, 1} };
@Parameter(0) public int expected; @Parameter(1) public int input1; @Parameter(2) public int input2;
@Test public void testAddition() { assertEquals(expected, add(input1, input2)); }
private int add(int m, int n) { return m+n; } }
58
xUnit JUnit is one of many in
the xUnit family…
NUnit It is an open source unit
testing framework for Microsoft .NET
It serves the same purpose as JUnit does in the Java world
59
“The tip of the iceberg” Coverage testing
A little … Integration testing
A little … System/GUI testing
Jemmy, Abbot, JFCUnit, … Testing legacy applications Testing J2EE applications Testing database applications Testing Web applications
HttpUnit, JWebUnit, … Testing Web services (SOA) …
Possible exercises at the exam Given a class and a “Test specification” writing some
Junit test methods Ex. Stack or Calculator
Refactoring some Junit test methods Ex. Adding SetUp() or re-modularize
Given a class and some Junit test methods “predict” the result Ex. CurrentAccount
Writing a Mock object or/and a Junit test method using a mock object Ex. Calendar
60
61
References (used to prepare these slides)
1. Reference WebSite: www.junit.org 2. Quick and easy introduction
http://clarkware.com/articles/JUnitPrimer.html 3. JUnit in Action. Book. T. Husted and V. Massol. 4. Pragmatic Unit Testing. Book. A. Hunt and David Thomas. 5. Test Infected: Programmers Love Writing Tests, Kent Beck,
Eric Gamma, http://junit.sourceforge.net/doc/testinfected/testing.htm 6. JUnit 4.0 in 10 minutes http://www.instrumentalservices.com/media/
articles/java/junit4/JUnit4.pdf 7. Get Acquainted with the New Advanced Features of JUnit
4, by Antonio Goncalves, http://www.devx.com/Java/Article/31983
62
… use JUnit
“Keep the bar green to keep the code clean…”
Top Related