Writing Tests in JUnit - STAR-LABenrico/mmis/Testing_2_JUNIT.pdf · 3 Testing with JUnit Junit is a...

Preview:

Citation preview

1

Writing Tests in JUnit (vers. 3.8)

Filippo Ricca DISI, Università di Genova, Italy

ricca@disi.unige.it

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

Recommended