Writing Tests in JUnit

62
1 Writing Tests in JUnit (vers. 3.8)

Transcript of Writing Tests in JUnit

Page 1: Writing Tests in JUnit

1

Writing Tests in JUnit (vers. 3.8)

Filippo Ricca DISI, Università di Genova, Italy

[email protected]

Page 2: Writing Tests in JUnit

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

Page 3: Writing Tests in JUnit

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

Page 4: Writing Tests in JUnit

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

Page 5: Writing Tests in JUnit

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(…)

Page 6: Writing Tests in JUnit

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

Page 7: Writing Tests in JUnit

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

Page 8: Writing Tests in 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 …

Page 9: Writing Tests in JUnit

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

Page 10: Writing Tests in JUnit

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”

Page 11: Writing Tests in JUnit

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()); } }

Page 12: Writing Tests in JUnit

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 …”

Page 13: Writing Tests in JUnit

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”

Page 14: Writing Tests in JUnit

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

Page 15: Writing Tests in JUnit

15

Test of “Exceptions”

Page 16: Writing Tests in JUnit

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 …”

Page 17: Writing Tests in JUnit

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

{ /*... */ } }

Page 18: Writing Tests in JUnit

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(…) {...} }

Page 19: Writing Tests in JUnit

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()); } }

Page 20: Writing Tests in JUnit

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 ....

Page 21: Writing Tests in JUnit

21

Run as JUnit Test   Run

  Run As   Junit Test

Page 22: Writing Tests in JUnit

22

Red / Green Bar

expected <-3> but was <-4>

StackTester 28: assertEquals( -3, aStack.pop() );

actual

expected actual

Page 23: Writing Tests in JUnit

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 …

Page 24: Writing Tests in JUnit

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

Page 25: Writing Tests in JUnit

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 …

Page 26: Writing Tests in JUnit

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!

Page 27: Writing Tests in JUnit

27

Page 28: Writing Tests in JUnit

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”

Page 29: Writing Tests in JUnit

29

Page 30: Writing Tests in JUnit

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); } }

Page 31: Writing Tests in JUnit

31

Page 32: Writing Tests in JUnit

32

Run Junit (first time)

Page 33: Writing Tests in JUnit

33

Page 34: Writing Tests in JUnit

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); } }

Page 35: Writing Tests in JUnit

35

Page 36: Writing Tests in JUnit

36

Run Junit (second time)

Page 37: Writing Tests in JUnit

37

Page 38: Writing Tests in JUnit

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); } }

Page 39: Writing Tests in JUnit

39

Run JUnit (third time) Run time error

Page 40: Writing Tests in JUnit

40

Page 41: Writing Tests in JUnit

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”

Page 42: Writing Tests in JUnit

42

Page 43: Writing Tests in JUnit

43

Run JUnit (fourth time)

Page 44: Writing Tests in JUnit

44 “I can refactoring the program without anxiety. I have the testcases …”

Page 45: Writing Tests in JUnit

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

Page 46: Writing Tests in JUnit

46

Run JUnit (fifth time)

Page 47: Writing Tests in JUnit

47

“The end”

Page 48: Writing Tests in JUnit

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”

Page 49: Writing Tests in JUnit

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

Page 50: Writing Tests in JUnit

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*()

Page 51: Writing Tests in JUnit

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

Page 52: Writing Tests in JUnit

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

Page 53: Writing Tests in JUnit

Mock objects example (1) Class under test Interface

Real class used

Mock class

Page 54: Writing Tests in JUnit

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

Page 55: Writing Tests in JUnit

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()

Page 56: Writing Tests in JUnit

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);}

}

Page 57: Writing Tests in JUnit

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; } }

Page 58: Writing Tests in JUnit

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

Page 59: Writing Tests in JUnit

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)   …

Page 60: Writing Tests in JUnit

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

Page 61: Writing Tests in JUnit

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

Page 62: Writing Tests in JUnit

62

… use JUnit

“Keep the bar green to keep the code clean…”