93
Presenter Name Presenter Title Presentation Date Jonathan Lalou Software Architect Agile Development Scrum Master January 24th, 2012 Test Driven Development Testing Cooking Book

Test Driven Development Testing Cooking Book

Embed Size (px)

DESCRIPTION

Test Driven Development Testing Cooking Book. Jonathan Lalou Software Architect Agile Development Scrum Master January 24th, 2012. Disclaimer on this training session. Expected attendees level: beginner / middle Java as well as C++, DotNet, etc. No difficulty on Java Slow rise Targets: - PowerPoint PPT Presentation

Citation preview

Page 1: Test Driven Development Testing Cooking Book

Presenter Name

Presenter Title

Presentation

Date Jonathan LalouSoftware ArchitectAgile DevelopmentScrum Master

January 24th, 2012

Test Driven DevelopmentTesting Cooking Book

Page 2: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Disclaimer on this training session

Expected attendees level: beginner / middle Java as well as C++, DotNet, etc. No difficulty on Java

Slow rise Targets:

Search, think, try, investigate… no solution “out of the box” Transmit best practices Give return of experiences Improve your skills

Needed: Brain, pencil, paper IDE (Eclipse, IDEA) Jars: JUnit, EasyMock, Log4j

Planned duration: 2h

Page 3: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

1. Test Driven Development1. Concepts

2. Interest

3. Among other Agile methods

2. Unit Test Cooking Book1. Basics

2. Methods within methods

3. Layers / Mocks

4. Exceptions

5. Private methods

3. Other Testsa. Integration tests

b. Runtime tests

c. Stress tests

4. Limitations and Vulnerabilities

Plan

Page 4: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

MySelf!

Jonathan LALOU Mines Nancy 2000-2003 Cadextan / SunGard since 2005 BNP Arbitrage since 2007:

PW European PB

Page 5: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

BKN Agility

Business Knowledge Network Agility

Scrum General (2 sessions, once a month) Scrum master Product Owner

TDD (1 session, once a month) Continuous Integration Lean Etc.

For Sungard consultants For Sungard customers

Page 6: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Test Driven Development

Page 7: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Concepts

Write test before implementation Lifecycle

1. Write a test2. Check it fails3. Write minimum code to make the test succeed4. Check it succeeds5. Refactor

Page 8: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Interest

Improve specifications Provide validation for development Less debug Less regression Safe refactoring Automatization

Page 9: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Among Other Agile Methods

Other famous Agile methods: Scrum, XP, Continuous Integration

XP loves TDD One codes the test One codes the needed program

Continuous Integration Runs complete test set Raises errors soon

Version Control System easy roll back

Page 10: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Experience…

Covertures: 80% Need update To be run before each commit Strategy

Quick and dirty: With private methods At the bottom of the very class Need compilation

“No private” methods In the same package, in another folder

Page 11: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Unit Test Cooking Book

Page 12: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Basics for Beginners

Page 13: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Basic (1)

Example: “write a service that sums two integers”

Page 14: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Basic (2)

Mode “I’ve just got out of school”:1 public class Summer {

2 public Integer sum(Integer a, Integer b){

3 return a+b;

4 }

5

6 public static void main(String[] args){

7 int foo = new Summer().sum(12, 5);

8 if (foo != 17){

9 System.err.println("There is an error");

10 }else{

11 System.out.println("this is OK");

12 }

13

14 }

15 }

Page 15: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Basic (3)

Mode “TDD”

1/ Create empty class Summer:1 public class Summer {

2 public Integer sum(Integer a, Integer b){

3 return null;

4 }

2/ Create unit test:

1 public class SummerUnitTest extends TestCase {

2 public void testSum() throws Exception {

3 assertEquals(17, new Summer().sum(15, 2));

4 }

5 }

3/ Complete class Summer:1 public class Summer {

2 public Integer sum(Integer a, Integer b){

3 return a+b;

4 }

Page 16: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Basic (4)

Framework JUnit Open source, quasi standard Integration within IntelliJ IDEA, Eclipse, Netbeans, etc. Integration with Maven (plugin Surefire) Galaxy of “XUnit”: NUnit, PHPUnit, PyUnit, etc. Other framework: TestNG, etc.

JUnit 3 setUp() tearDown() void test*()

JUnit 4 @Before @After @Test

Page 17: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Basic (5): Expectations

Expectations: Success Failure Error

Asserts: assertEquals assertSame assertTrue / assertFalse assertNull / assertNotNull fail

Page 18: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 1

Write a class with following services• get the maximum of two integers• get the maximum of an array of integers• flip an array of integers• says whether a String is a valid email address

(please do not use Apache Commons, Spring, etc. in unit tests exercises)

Page 19: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 1: correction

public class Exercise1 { public Integer maxOfTwo(Integer a, Integer b) { return a > b ? a : b; }

public Integer maxOfArray(int[] args) { /*if (args == null || args.length == 0) { return null; }*/ Integer max = Integer.MIN_VALUE; for (int i = 0; i < args.length; i++) { max = maxOfTwo(max, args[i]); } return max; }

public int[] flip(int[] source) { final int[] answer = new int[source.length]; for (int i = 0; i < source.length; i++) { answer[i] = source[source.length - i - 1]; } return answer; }}

Page 20: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 1: correction (JUnit 3)

public class Exercise1UnitTestJUnit3 extends TestCase{private Exercise1 exercise1;

protected void setUp() { exercise1 = new Exercise1(); }

protected void tearDown() { exercise1 = null; }

public void testMaxOfTwo() throws Exception { Assert.assertEquals(5, exercise1.maxOfTwo(4, 5).intValue()); }

public void testMaxOfArray() { Assert.assertEquals(15, exercise1.maxOfArray(new int[]{5, 6, 3, 15, 4, 9,

14}).intValue()); }

public void testFlip() { final int[] source = new int[]{1, 2, 5, 4, 6}; final int[] expectedAnswer = new int[]{6, 4, 5, 2, 1}; final int[] actualAnswer = exercise1.flip(source); Assert.assertNotNull(actualAnswer); Assert.assertEquals(actualAnswer.length, source.length); for (int i = 0; i < actualAnswer.length; i++) { Assert.assertEquals(expectedAnswer[i], actualAnswer[i]); } }

}

Page 21: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 1: correction (JUnit 4)

public class Exercise1UnitTest {

private Exercise1 exercise1;

@org.junit.Before

public void methodBefore() {

exercise1 = new Exercise1();

}

@org.junit.After

public void methodAfter() {

exercise1 = null;

}

@Test

public void maxOfTwo() throws Exception {

Assert.assertEquals(5, exercise1.maxOfTwo(4, 5).intValue());

}

@Test

public void maxOfArray() {

Assert.assertEquals(15, exercise1.maxOfArray(new int[]{5, 6, 3, 15, 4, 9, 14}).intValue());

}

@Test

public void flip() {

final int[] source = new int[]{1, 2, 5, 4, 6};

final int[] expectedAnswer = new int[]{6, 4, 5, 2, 1};

final int[] actualAnswer = exercise1.flip(source);

Assert.assertNotNull(actualAnswer);

Assert.assertEquals(actualAnswer.length, source.length);

for (int i = 0; i < actualAnswer.length; i++) {

Assert.assertEquals(expectedAnswer[i], actualAnswer[i]);

}

}

}

Page 22: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Best Practices (1)

For each class X, create a class XUnitTest extends TestCase

X in src/ folder, XUnitTest in test/unit Use any value, rather than basic values (0, 1)

546541 95.1215 “myVariableName”

Beware of: Arrays and Collections asserts on elements

Page 23: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 2

Write following services:• Get the absolute value of a Double• Get f: x x²• Get g: (x,y) y(xy)

• Reminder: • x < 0 |x| = -x • x >= 0 |x| = +x

Page 24: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 2: Correction (1)

package lalou.jonathan.tdd;

/** * User: Jonathan Lalou * Date: 9/22/11 * Time: 1:53 PM * $Id$ */public class Exercise2 { public Double absoluteValue(Double x) { if (x > 0) return x; else return -x; }

public Double f(Double x){ return Math.sqrt(Math.pow(x, 2)); }

public Double g (Double x, Double y){ return Math.pow(Math.pow(x, y), 1/y); }

}

Page 25: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 2: Correction (2)

public class Exercise2UnitTest {private Exercise2 exercise2;

@Before public void setUp(){ exercise2 = new Exercise2(); }

@Test public void testAbsoluteValue_positive() throws Exception { Assert.assertEquals(3.2, exercise2.absoluteValue(3.2)); }

// the second test IS needed @Test public void testAbsoluteValue_negative() throws Exception { Assert.assertEquals(4.5, exercise2.absoluteValue(-4.5)); }

@Test public void testF() throws Exception { final double expected = 6.9999; Assert.assertEquals(expected, exercise2.f(expected)); }// …}

Page 26: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 2: Correction (3)

@Test public void testG() throws Exception { final double expected = 6.9999999; final Double power = 99.99987654; Assert.assertEquals(expected, exercise2.g(expected, power)); }

… junit.framework.AssertionFailedError: expected:<6.9999999> but

was:<6.999999899999999>at lalou.jonathan.tdd.Exercise2UnitTest.testG(Exercise2UnitTest.java:41)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

(…)at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

(…)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Process finished with exit code -1

Page 27: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 2: Correction (4)

@Test

public void testG() throws Exception {

final double expected = 6.9999999;

final Double power = 99.99987654;

final Double epsilon = 0.1;

Assert.assertEquals(

expected,

exercise2.g(expected, power),

epsilon);

}

Page 28: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Best Practices (2)

Test every possible switch Beware of:

Double, Float, etc. add an epsilon

Page 29: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 3

A complex number: (x, y) / (ρ,θ) Write the method that gives the square of the module Write unit test

Page 30: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 3: Correction (1)

1 public class Complex {

2 private final Double x, y, rho, theta;

3

4 public Complex(Double x, Double y, Double rho, Double theta) {

5 this.rho = rho;

6 this.theta = theta;

7 this.x = x;

8 this.y = y;

9 }

10

11 public Double getSquaredModule(){

12 return x*x + y*y;

13 }

14 }

Page 31: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 3: Correction (2)

1 public void testGetSquaredModule_firstAttempt() { 2 final Double expected = 5.0, epsilon = 0.01; 3 final Complex complex = new Complex(1.0, 2.0,

Math.sqrt(5), 1.10714872); 4 final Double actual = complex.getSquaredModule(); 5 assertEquals(expected, actual, epsilon); 6 } 7 8 public void testGetSquaredModule_secondAttempt() { 9 final Double expected = 5.0, epsilon = 0.01; 10 final Complex complex = new Complex(0.0, 0.0,

Math.sqrt(5), 1.10714872); 11 final Double actual =

complex.getSquaredModule(); 12 assertNotSame(expected, actual);

13 }

Page 32: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 3: Correction (3)

1 public void testGetSquaredModule_right() {

2 final Double expected = 5.0, epsilon = 0.01;

3 final Complex complex = new Complex(1.0, 2.0, 0.0,

0.0);

4 final Double actual = complex.getSquaredModule();

5 assertEquals(expected, actual, epsilon);

6 }

Page 33: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Best Practices (3)

Setup the minimum of needed fields

Page 34: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Methods within a method (1)

Errors can offset each other Idea:

test only one method at a time override / mock other methods check other methods were called with rights parameters

Not “pure-TDD” (tests before code) Many different ways to test the same code

Page 35: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Methods within a method (2)

1 public class MethodWithinMethod {

2

3 public Integer sum(Integer... params) {

4 Integer answer = 0;

5 for (Integer param : params) {

6 answer += param;

7 }

8 return answer;

9 }

10

11 public Integer sumAndQuadruple(Integer... params) {

12 final Integer summed = sum(params);

13 return 4 * summed;

14 }

15 }

Page 36: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Methods within a method (3)

1 @Test

2 public void testSum() throws Exception {

3 assertEquals(15, methodWithinMethod.sum(1, 2, 3, 4, 5).intValue());

4 }

5

6 @Test

7 public void testSumAndQuadruple_bad() throws Exception {

8 assertEquals(60, methodWithinMethod.sumAndQuadruple(1, 2, 3, 4, 5).intValue());

9 }

Page 37: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Methods within a method (4)

1 @Test 2 public void testSumAndQuadruple_good() throws Exception { 3 final String OK = "OK"; 4 final Integer[] params = new Integer[]{1, 2, 3, 4, 5}; 5 final Integer summed = -1; 6 final StringBuffer sb = new StringBuffer();

7 methodWithinMethod = new MethodWithinMethod(){ 8 public Integer sum(Integer... _params) { 9 assertEquals(_params, params); 10 sb.append(OK); 11 return summed; 12 } 13 };

14 assertEquals(0, sb.toString().length());15 // before calling method16 assertEquals("", sb.toString());17 assertEquals(-4, methodWithinMethod.sumAndQuadruple(params).intValue()); 18 assertEquals(OK, sb.toString());

19 }

Page 38: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 5: method within method

Write a class with two methods First: to log input/outputs Second: logs and inverses a non null Integer (and returns 0 for

0)

(excercice#5 is before #4 ;-) )

Page 39: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 5 : correction (1)

1 public class Exercise5 {

2 public void log(String message) {

3 System.out.println(message);

4 }

5

6 public Double inverse(Integer param) {

7 final Double answer;

8 log("called with parameter: " + param);

9 answer = param == 0 ? 0.0 : 1 / (double) param;

10 log("exit with result: " + answer);

11 return answer;

12 }

13 }

Page 40: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 5 : correction (2)

public void testInverse() {final List<String> witness = new ArrayList<String>(2);final Exercise5 exercise5 = new Exercise5() {public void log(String message) {

assertTrue(message.equalsIgnoreCase("called with parameter: 4")|| message.equalsIgnoreCase("exit with result: 0.25"));

witness.remove(message);}

};final Integer param = 4;final Double expectedAnswer = 0.25;final Double actualAnswer;

witness.add("called with parameter: 4");witness.add("exit with result: 0.25");

// check before runassertFalse(witness.isEmpty());actualAnswer = exercise5.inverse(param);assertNotNull(actualAnswer);assertEquals(expectedAnswer, actualAnswer, 0.000001);// check after runassertTrue(witness.isEmpty());

}

Page 41: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Advanced

Page 42: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks

Page 43: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Layers

Testing layers should deserve a complete training session!

Unit test: test only one class behavior: Service

DAO Logic EJB

Presentation Form Action

Architecture in layers: Unit test strategy:

Unit test each layer Mock other layers

Integration tests

Page 44: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks

Use case / interest: Non-deterministic results Precalculate state Fastness Not yet implemented actual class

Mocks ~ AOP

Mocks Time consuming!

Page 45: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks / Stubs / Fakes

Mock Stub

Provide minimal implementation

Fake Emulate a service, eg: client/server

Page 46: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks: frameworks in Java

Mockit Mockito EasyMock JMock PowerMock …

Page 47: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks: EasyMock (old): setup

1 private TreeNodeDetailVarDao treeNodeDetailVarDao;

3 private MockControl<TreeNodeDetailVarDao> treeNodeDetailVarDaoMockControl;

10 @Before()

11 public void setUp() throws Exception {

12 treeNodeDetailVarDaoMockControl = MockControl.createStrictControl(TreeNodeDetailVarDao.class);

13 treeNodeDetailVarDao = treeNodeDetailVarDaoMockControl.getMock(); 14 }

Page 48: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks: EasyMock (old): test

treeNodeDetailVarDao.getTreeNodeDetailVarByTreeNodeId(treeNodeId);

treeNodeDetailVarDaoMockControl.setReturnValue(12345);

treeNodeDetailVarDaoMockControl.replay();

genericDtoService.getVarDistributionTOByTreeNodeId(treeNodeId, numberOfBars);

treeNodeDetailVarDaoMockControl.verify();

Page 49: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks: EasyMock (new)

“A la” Mockito

1 expect(

2 shiftListDao.findLastUpdateDateForShiftTypeAndValueDate(ShiftType.STRESS_VAR, valueDate)).

3 andReturn(updateDateInDB);

4

5 // shiftListDao.findLastUpdateDateForShiftTypeAndValueDate(ShiftType.STRESS_VAR, valueDate);

6 // shiftListDaoMockControl.setReturnValue(updateDateInDB);

Page 50: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercice 4

Three DAOs PeopleDAO PortfolioDAO InstrumentPriceDAO

One service that gets the value of someone’s portfolio One service that gets the total value all portfolios

Page 51: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (1) Entities

public class People {

public Integer id;

public String name;

}

public class Portfolio {

public Integer id;

public Integer peopleId; // foreign key on People

public List<Integer> instrumentIds;

}

public class Instrument {

public Integer id;

public Double price;

public String name;

}

Page 52: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (2) Interfaces

public interface PortfolioDAO {

Portfolio findByPeopleId(Integer peopleId) throws IllegalStateException;

// should throw "PortfolioNotFoundException" IRL

}

public interface PeopleDAO {

List<People> findAll();

void helloWorld();

}

public interface InstrumentDAO {

Instrument findById(Integer pk) throws IllegalStateException;

// should throw "InstrumentNotFoundException" IRL

}

Page 53: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (3) Code

public class Exercise4 { public PeopleDAO peopleDAO; public InstrumentDAO instrumentDAO; public PortfolioDAO portfolioDAO;

public Exercise4(InstrumentDAO instrumentDAO, PeopleDAO peopleDAO, PortfolioDAO portfolioDAO) {

this.instrumentDAO = instrumentDAO; this.peopleDAO = peopleDAO; this.portfolioDAO = portfolioDAO; }

Page 54: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (4) Methods

1 public Double getPortfolioValue(Integer peopleId){ 2 final Portfolio portfolio; 3 Double answer = 0.0; 4 portfolio = portfolioDAO.findByPeopleId(peopleId); 5 for (Integer instrumentId : portfolio.instrumentIds) { 6 final Instrument instrument; 7 instrument = instrumentDAO.findById(instrumentId); 8 answer += instrument.price; 9 } 10 return answer; 11 } 12 13 public Double getAllPortfolioValue(){ 14 final List<People> allPeople; 15 Double answer = 0.0; 16 17 allPeople = peopleDAO.findAll(); 18 for (People people : allPeople) { 19 answer += getPortfolioValue(people.id); 20 } 21 peopleDAO.helloWorld(); 22 23 return answer; 24 }

Page 55: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (5) Unit Tests Setup

public class Exercise4UnitTest extends TestCase { final double epsilon = 0.0001;

private PeopleDAO peopleDAO; private InstrumentDAO instrumentDAO; private PortfolioDAO portfolioDAO;

private MockControl<PeopleDAO> peopleDAOControl; private MockControl<InstrumentDAO> instrumentDAOControl; private MockControl<PortfolioDAO> portfolioDAOControl;

private Exercise4 exercise4;

@Before public void setUp() throws Exception { peopleDAOControl = MockControl.createStrictControl(PeopleDAO.class); instrumentDAOControl = MockControl.createStrictControl(InstrumentDAO.class); portfolioDAOControl = MockControl.createStrictControl(PortfolioDAO.class);

peopleDAO = peopleDAOControl.getMock(); instrumentDAO = instrumentDAOControl.getMock(); portfolioDAO = portfolioDAOControl.getMock();

exercise4 = new Exercise4(instrumentDAO, peopleDAO, portfolioDAO); }

Page 56: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (6) Mocks Setup

private void mockReplay() {

peopleDAOControl.replay();

instrumentDAOControl.replay();

portfolioDAOControl.replay();

}

private void mockVerify() {

peopleDAOControl.verify();

instrumentDAOControl.verify();

portfolioDAOControl.verify();

}

Page 57: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (7) Unit test#1

@Test public void testGetPortfolioValue() throws Exception { final Integer peopleId = 5610; final Portfolio portfolio = new Portfolio(); final Instrument instrument1 = new Instrument(); final Instrument instrument2 = new Instrument(); final int instrumentId1 = 147; final int instrumentId2 = 369; final List<Integer> instrumentIds = Arrays.asList(instrumentId1, instrumentId2); final Double expectedAnswer = 57.9; final Double actualAnswer;

instrument1.id = instrumentId1; instrument2.id = instrumentId2; instrument1.price = 12.3; instrument2.price = 45.6;

portfolio.instrumentIds = instrumentIds;

portfolioDAO.findByPeopleId(peopleId); portfolioDAOControl.setReturnValue(portfolio);

instrumentDAO.findById(instrumentId1); instrumentDAOControl.setReturnValue(instrument1);

instrumentDAO.findById(instrumentId2); instrumentDAOControl.setReturnValue(instrument2);

mockReplay(); actualAnswer = exercise4.getPortfolioValue(peopleId); mockVerify();

assertEquals(expectedAnswer, actualAnswer, epsilon); }

Page 58: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 4: correction (8) Unit test#2

@Test public void testGetAllPortfolioValue() throws Exception { final Integer id1 = 654, id2 = 987; final People someone = new People(); final People someoneElse = new People(); final List<People> allPeople; final Double expectedAnswer = 2.02; final Double actualAnswer;

someone.id = id1; someoneElse.id = id2;

allPeople = Arrays.asList(someone, someoneElse);

exercise4 = new Exercise4(instrumentDAO, peopleDAO, portfolioDAO) { public Double getPortfolioValue(Integer peopleId) { assertTrue((id1 == peopleId) || (id2 == peopleId)); // not a relevant test!! return 1.01; } };

peopleDAO.findAll(); peopleDAOControl.setReturnValue(allPeople);

peopleDAO.helloWorld(); peopleDAOControl.setVoidCallable();

mockReplay(); actualAnswer = exercise4.getAllPortfolioValue(); mockVerify(); assertEquals(expectedAnswer, actualAnswer, epsilon); }

Page 59: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Mocks: advanced

Controls: Nice controls Controls Strict controls

Matchers Number of calls Ignore fields

(eg: timestamps for / from a DB)

Return value and/or throw Exception!

Mock interfaces and even concrete classes

Page 60: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Private Methods

Page 61: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Private methods

2 strategies: (almost) All methods are at least protected Use Java reflexivity APIs, eg:

1 ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();

2 final Field privateThreadsField;

3 privateThreadsField = ThreadGroup.class.getDeclaredField( “threads”);

5 privateThreadsField.setAccessible(true);

6 threads = (Thread[]) privateThreadsField.get(threadGroup);

Page 62: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 6: Private Methods

Test this class:

1 public class Exercise6 {

2

3 private Integer increment(Integer i){

4 return i++;

5 }

6

7 private Integer decrement(Integer i){

8 return i--;

9 }

10 }

Page 63: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 6: Correction (1)

1 public class Exercise6UnitTest {

2 private Exercise6 exercise6;

3 private Method methodIncrement, methodDecrement;

4

5 @Before

6 public void setUp() throws Exception {

7 exercise6 = new Exercise6();

8

9 methodIncrement = Exercise6.class.getDeclaredMethod("increment", Integer.class);

10 methodIncrement.setAccessible(true);

11

12 methodDecrement = Exercise6.class.getDeclaredMethod("decrement", Integer.class);

13 methodDecrement.setAccessible(true);

14 } …15 }

Page 64: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercice 6: Correction (2)

1 @Test

2 public void testIncrement() throws InvocationTargetException, IllegalAccessException {

3 final Integer param = 9;

4 final Integer expected = 10;

5 final Integer actual = (Integer) methodIncrement.invoke(exercise6, param);

6

7 assertEquals(expected, actual);

8 }

9

10 @Test

11 public void testDecrement() throws InvocationTargetException, IllegalAccessException {

12 final Integer param = 9;

13 final Integer expected = 8;

14 final Integer actual = (Integer) methodDecrement.invoke(exercise6, param);

15

16 assertEquals(expected, actual);

17 }

Page 65: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

java.lang.Exceptions

Page 66: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

java.lang.Exceptions (1)

Why to test exception? Exceptions are a kind of returned values Testing “KO cases” as well as “OK cases”

JUnit 3 Try/catch

JUnit4 @Test(expected=“…Exception”)

You can’t use both in the same JUnit Test Case!

Page 67: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exceptions (2)

1 public void testArrayIndexOutOfBoundsException() {

2 final Integer[] array = new Integer[]{1, 2, 3};

3 try {

4 final Integer foo = array[18];

5 fail();

6 } catch (ArrayIndexOutOfBoundsException aioobe) {

7 // test OK

8 }

9 }

11 @Test(expected = ArrayIndexOutOfBoundsException.class)

12 public void testArrayIndexOutOfBoundsException_junit4() {

13 final Integer[] array = new Integer[]{1, 2, 3};

14 final Integer foo = array[18];

15 }

Page 68: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 7: Exceptions

Write a parser of integers and its unit test

1 public Integer parseString(String szInt)

throws ParseException {

2 return Integer.parseInt(szInt);

3 }

Page 69: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 7: Correction

1 private Exercise7 exercise7;

3 @Before

4 public void before(){

5 exercise7 = new Exercise7();

6 }

7

8 @Test

9 public void testParseString_OK() throws ParseException {

10 final String szInt = "123456";

11 final Integer expected = 123456;

12 Assert.assertEquals(expected, exercise7.parseString(szInt));

13 }

14

15 // @Test(expected = Exception.class)

16 @Test(expected = NumberFormatException.class)

17 public void testParseString_KO() throws ParseException {

18 final String szInt = "thisIsNotAnInteger";

19 exercise7.parseString(szInt);}

Page 70: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Loggers

Page 71: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Loggers (1)

Eg:

1 public class ClassWithLogger {

2 private static final Logger LOGGER = Logger.getLogger(ClassWithLogger.class);

3

4 public void printMessage(Integer foo){

5 LOGGER.warn("this is the message#" + foo);

6 }

7 }

Page 72: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Loggers (2)

Idea: play on appenders Consider a List of Strings as an appender Discard other appenders

Page 73: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Loggers (3)

Eg:1 public class TestAimedAppender extends ArrayList<String> implements Appender { 2 private final Class clazz; 3 4 public TestAimedAppender(Class clazz) { 5 super(); 6 this.clazz = clazz; 7 }

public void close() {} 24

25 @Override 26 public void doAppend(LoggingEvent event) { 27 add(event.getRenderedMessage()); 28 } 29 30 @Override 31 public String getName() { 32 return "TestAppender for " + clazz.getSimpleName(); 33 } public boolean requiresLayout() { 58 return false; 59 } ...

60 }

Page 74: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 8: Loggers

Test the following method, discriminating on log level:1 public class Exercise8 {

2 private static final Logger LOGGER = Logger.getLogger(Exercise8.class);

3

4 public void printAnotherMessage(Integer foo) {

5 if (0 == foo % 2) {

6 LOGGER.info("I am an even number: " + foo);

7 } else {

8 LOGGER.warn("I am an odd number: " + foo);

9 }

10 if (0 == foo % 3) {

11 LOGGER.error(foo + " is dividable by 3");

12 }

13 }

14 }

Page 75: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 8: correction

Page 76: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

new Date()

Page 77: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercice 9:

Unit test this method that raises an exception if the Date given as parameter is anterior to current day

(mind the day, don’t mind hour/minutes/seconds)

public void checkDate(Date startValueDate) throws IllegalArgumentException {

if (startValueDate.before(new Date())) {

throw new IllegalArgumentException();

}

}

Page 78: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 9: correction (1)

public class Exercise9 {

public void checkDate(Date startValueDate) throws IllegalArgumentException{if (startValueDate.before(today())) {

throw new IllegalArgumentException();}

}

protected Date today() {return new Date();

}}

Page 79: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 9: correction (2)

public class Exercise9UnitTest extends TestCase {

private Exercise9 exercise9;private SimpleDateFormat dateFormat = new

SimpleDateFormat("yyyyMMdd");private Date today;private Date yesterday;private Date tomorrow;

protected void setUp() throws Exception {today = dateFormat.parse("20120124");yesterday = dateFormat.parse("20120123");tomorrow = dateFormat.parse("20120125");exercise9 = new Exercise9() {

@Overrideprotected Date today() {

return today;}

};}

Page 80: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Exercise 9: correction (3)

@Testpublic void testCheckDate_OK() {

try {exercise9.checkDate(tomorrow);

} catch (IllegalArgumentException e) {fail();

}}

@Testpublic void testCheckDate_KO() {

try {exercise9.checkDate(yesterday);fail();

} catch (IllegalArgumentException e) {// test OK}

}

Page 81: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Other Tests

Page 82: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Integration Tests

Test part or all of a complex chain Test in real life situations, not theory behaviors

Page 83: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Runtime Tests

Concepts Examples

DB with Hibernate EJB

(loggers admited in runtime tests)

Page 84: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Runtime Test: JDBC query onto Oracle (1)

1 <?xml version="1.0" encoding="UTF-8"?>

2 <beans xmlns=“…”>

7 <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">

8 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>

9 <property name="url" value="jdbc:oracle:thin:@host:1234:SCHEMA"/>

10 <property name="username" value=“myUserName"/>

11 <property name="password" value=“myPassword"/>

12 <property name="initialSize" value="2"/>

13 <property name="minIdle" value="2"/>

14 </bean>

15

16 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

17 <property name="dataSource" ref="dataSource"/>

18 </bean> 20 </beans>

Page 85: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Runtime Test: JDBC query onto Oracle (2)

public class FloatArrayResultRuntimeTest extends TestCase { 23 private static final Logger LOGGER = Logger.getLogger(FloatArrayResultRuntimeTest.class); private static final String TABLE = "RL_POSITIONRESULTSTRESS"; 30 private static final String PK_FIELD = "POSITIONRESULTSTRESSID"; private static final String BLOB_FIELD =

"stress"; private static final int[] PK_VALUES = {3242, 3247, 3285, 3284}; 33 34 private ApplicationContext applicationContext; 35 private JdbcTemplate jdbcTemplate; 36 37 @Before 38 public void setUp() throws Exception { 39 applicationContext = new ClassPathXmlApplicationContext( 40 "com/bnpp/pb/risklayer/model/domain/FloatArrayResultRuntimeTest-applicationContext.xml"); 41 assertNotNull(applicationContext); 42 jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate"); 43 assertNotNull(jdbcTemplate); 44 } 50 @Test 51 public void testGetArray() throws Exception { 52 for (int pk_value : PK_VALUES) { 53 final Blob stressBlob; 54 final byte[] stressBytes; 55 final double[] doubles; 57 stressBlob = (Blob) jdbcTemplate.queryForObject("select " + BLOB_FIELD + " from " + TABLE + " where " +

PK_FIELD + " = " + pk_value, Blob.class); 58 assertNotNull(stressBlob); 59 stressBytes = stressBlob.getBytes(1, (int) stressBlob.length()); 60 doubles = NumericArrayHelper.unzipFloatArrayAsDoubleArray(stressBytes); 61 62 LOGGER.info("Blob size: " + doubles.length); 63 LOGGER.info(ToStringBuilder.reflectionToString(doubles)); 64 } 65 }

66 }

Page 86: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Runtime Test: call a remote EJB (1): Spring

1 <bean id="loaderSessionRemote"

2 class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">

3 <property name="jndiName" value="ejb.backend.generic-data-loader-session-bean"/>

4 <property name="businessInterface" value="com.bnpparibas.primeweb.backend.services.GenericDataLoaderSession"/>

5 <property name="jndiEnvironment">

6 <props>

7 <prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>

8 <prop key="java.naming.provider.url">t3://localhost:1234</prop>

9 <prop key="java.naming.security.principal">myLogin</prop>

10 <prop key="java.naming.security.credentials">myPassword</prop>

11 <prop key="weblogic.jndi.enableDefaultUser">true</prop>

12 </props>

13 </property>

14 </bean>

Page 87: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Runtime Test: call a remote EJB (2): Java

1 @Override

2 protected void setUp() throws Exception {

3 BeanFactory factory = new ClassPathXmlApplicationContext(

"applicationContext.xml",

this.getClass());

4 loaderSession = (GenericDataLoaderSession) factory.getBean("loaderSessionRemote");

5 super.setUp();

6 }

Page 88: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Stress Tests

Simulate behavior face to massive stimulation Validate scalability Identify bottle necks

Page 89: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Limitations and vulnerabilities

Page 90: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Limitations

Perfect for: service, DAO, technical, etc. Less suitable for

GUI Swing Web (Struts, GWT, etc.) Multi threads

Expensive Initial cost Maintenance cost

Page 91: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Conclusion

Page 92: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Who we are

Questions

Page 93: Test Driven Development Testing Cooking Book

www.sungard.com/consultingProprietary and Confidential. Not to be distributed or reproduced without permission

Links and contacts

Google! www.junit.org/ Oracle, Sun, Java sites Forums, mailing lists Trainer:

Twitter: @John_the_Cowboy Google+ : +Jonathan.Lalou [email protected] http://jonathan.lalou.free.fr