33
Introduction to Unit Testing and Tools. [TDD, Mockito, JUnit] How and what to UNIT test Brief introduction

How and what to unit test

Embed Size (px)

Citation preview

Introduction to Unit Testing and Tools. [TDD, Mockito, JUnit]

How and what to UNIT test

Brief introduction

00 how to use this stuffOrganisation

Purpose

• Introduce the art of unit testing, theory and some practice to fix concepts by doing.

Project organisation

• There are some components such as

• this presentation

• experiments discussed in sections EX (help the cat) related to code on GitHub

• verbose description of bug correction experiments and tdd

Use of colours

• By using colours jargon, focus and organisation are highlighted.

01 test better to be more agileAdvice One

Ninja level: 1000!!!

01 test categoriesAdvice One

Unit tests

• Focus on a single class by replacing real collaborators with test doubles.

• Isolated, very fast to execute, small.

Integration tests (not discussed)

• Focus on the proper integration of different modules of the code including uncontrolled code.

• Run slower than unit tests, require an application context and some resources. Usually the assert works on external resources (i.e. SQL query)

End-to-end tests (not discussed)

• Focus on the client’s point of view. Test doubles are usually not used, the point is to test the real system[1].

• Run really slow, significant amount of time is required.

01 system under test and collaboratorAdvice One

SUT - System Under Test

• Definition: part of the system being tested.

• Example: single class, single module or whole application (different granularity).

DOC - Depended On Component (or Collaborator)

• Definition: entity required by a SUT to achieve a goal, same granularity as the SUT.

• Example: service class to persist data, authentication module, etc.

01 code for everybody not just for youAdvice One

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live”

- Martin Golding

02 dominate the code by testsAdvice Two

Ponies are for pussies!

Unit tests

• Scope. Single class.

• Collaborators. Substituted with test doubles (fake replacements).

SUT is the class

view layer

DAO layer

business layer

02 definitionUnit Test

Goal

• Idea. Make sure the class you are working on right now works correctly [1].

• Test. Verify the expected behaviour of the class.

Test in isolation

• Forget about everything else. Focus on the single class which must work in any environment (no database, no web service, no additional classes or modules).

• Test fixture. Well known and fixed environment so that a test result is repeatable.

Benefits

• Great accuracy. Documentation of the single class code, more reliable than textual description.

• Run extremely fast. Immediate feedback on the code quality, bugs are immediately blocked.

• Easy refactor. Refactor your code with no panic, the class is covered by unit tests.

02 what and whyUnit Test

02 interactionsUnit Test

Direct interaction

• Between test class and SUT. Directly available in the source code.

• Can be controlled. Test class can control the interaction.

Indirect interaction

• Between SUT and DOCs. Cannot be controlled by the test class or client.

In general

• Inputs (direct and indirect). Set the SUT in a required state and invoke its methods.

• Outputs (direct and indirect). Used to verify if the SUT is working as expected.

Test Class(client) SUT DOCsdirect indirect

input

output

output

input

Test is NOT a unit test if [5]

• Its talks to the database

• It communicates across the network

• It touches the file system

• It can’t run at the same time as any of your other unit tests

• You have to do special thing to your environment (such as editing config files) to run it.

02 not a unit testUnit Test

Scaffolding according to the rules

03 escape from old patternsAdvice Three

!"" pom.xml#"" src !"" main $   !"" java $   $   #"" com $   $   #"" popcorntime $   $   #"" popcornapp $   $   !"" domain $   $   $   !"" AbstractEntity.java $   $   $   #"" Movie.java $   $   !"" repository $   $   $   #"" MovieRepository.java $   $   !"" service $   $   $   !"" MovieService.java $   $   $   #"" jpa $   $   $      #"" MovieServiceImpl.java $   $   #"" web $   !"" resources $   $   !"" log4j.properties $   $   #"" schema.sql $   #"" webapp $   !"" WEB-INF $   $   #"" web.xml $   #"" index.jsp $ #"" test !"" java $   #"" com $   #"" popcorntime $   #"" popcornapp $   #"" service $      #"" jpa $ !"" AbstractServiceImplTest.java $   #"" MovieServiceImplTest.java #"" resources #"" MovieServiceImplTest.csv

03 folder structureTest and Production Code

production code location

test code location

everything else than production code such as sql,log4j, etc.

everything else than test code such as cvs, log4j, etc

1 package com.contrastofbeauty.tutorial.teststructure; 2 3 import org.apache.log4j.Logger; 4 import org.junit.After; 5 import org.junit.Before; 6 import org.junit.Test; 7 8 public class CatTest { 9 10 private final static Logger logger = Logger.getLogger(CatTest.class); 11 12 private Cat cat; 13 14 @Before 15 public void setUp() throws Exception { 16 17 logger.info("Run setUp() to create a new fresh cat."); 18 cat = new Cat(); 19 } 20 21 @Test 22 public void testMethodOne() throws Exception { 23 24 logger.info("Now the method testMethodOne can be run, everything has been initialized."); 25 } 26 27 @After 28 public void tearDown() throws Exception { 29 30 logger.info("Run tearDown() to free cat resource."); 31 cat = null; 32 } 33 }

@Before methods are run before any test method but after any @Before method of super class.

03 unit test structure 1Test and Production Code

5 import … 14 15 public class CatPersistTest { 17 private final static Logger logger = Logger.getLogger(CatPersistTest.class); 19 private static final String CAT_FELIX = "felix"; 20 public static final long CAT_ID = 1L; 22 private static EntityManagerFactory emf; 27 private EntityManager em; 28 29 @BeforeClass 30 public static void setUpBeforeClass() throws Exception { 31 logger.info("SetUpBeforeClass."); 32 emf = Persistence.createEntityManagerFactory("h2"); 33 } 34 35 @AfterClass 36 public static void tearDownAfterClass() throws Exception { 37 logger.info("TearDownAfterClass."); 38 if (emf != null) { 39 emf.close(); 40 } 41 } 42 43 @Before 44 public void setUp() throws Exception { 45 logger.info("SetUp()."); 46 em = emf.createEntityManager(); 47 insertTestData(); 48 } 49 50 @After 51 public void tearDown() throws Exception { 53 logger.info("TearDown()."); 55 if (em != null) { 56 removeTestData(); 57 em.close(); 58 } 59 } 60

@BeforeClass run only once for all the tests in a class at the beginning

@AfterClass run only once for all the tests in a class at the end

@Before run once before each test

@After run once after each test

03 unit test structure 2Test and Production Code

61 @Test 62 public void testSave() throws Exception { 64 logger.info("A test."); 75 } 76 77 @Test 78 public void testVoid() throws Exception { 80 logger.info("Another test."); 81 } 82 83 private void insertTestData() throws Exception { 85 logger.info("...insert data"); 94 } 95 96 private void removeTestData() throws Exception { 98 logger.info("...remove data"); 107 } 108 }

@Test is simply a test

INFO [main] (DriverManagerConnectionProviderImpl.java:166) - HHH000401: using driver [org.h2.Driver] at URL [jdbc:h2:mem:test]INFO [main] (DriverManagerConnectionProviderImpl.java:172) - HHH000046: Connection properties: {user=sa}INFO [main] (DriverManagerConnectionProviderImpl.java:180) - HHH000006: Autocommit mode: falseINFO [main] (DriverManagerConnectionProviderImpl.java:102) - HHH000115: Hibernate connection pool size: 20 (min=1)INFO [main] (CatPersistTest.java:31) - SetUpBeforeClass.INFO [main] (CatPersistTest.java:45) - SetUp().INFO [main] (CatPersistTest.java:85) - ...insert dataINFO [main] (CatPersistTest.java:64) - A test.INFO [main] (CatPersistTest.java:53) - TearDown().INFO [main] (CatPersistTest.java:98) - ...remove data——— 2nd testINFO [main] (CatPersistTest.java:45) - SetUp().INFO [main] (CatPersistTest.java:85) - ...insert dataINFO [main] (CatPersistTest.java:80) - Another test.INFO [main] (CatPersistTest.java:53) - TearDown().INFO [main] (CatPersistTest.java:98) - ...remove dataINFO [main] (CatPersistTest.java:37) - TearDownAfterClass.INFO [main] (DriverManagerConnectionProviderImpl.java:281) - HHH000030: Cleaning up connection pool [jdbc:h2:mem:test]

Execution log

03 unit test structure 2Test and Production Code

Help the cat experimenting

There are some bugs inside class TweetCollector, correct them by creating a net of automatic unit test.

Target of the experiment

• See difference between doing unit tests to spot bugs and developing by debugging because of bugs propagation.

• Keep in mind the effort of doing unit test in test-last approach and compare with test-first approach AKA TDD (it will come next).

• Start using Mockito to mock collaborators and see the difference between using a mocking framework and overriding a method.

E1 find bugs by testingCoding Time

fake replacements

04 find the intruderAdvice Five

04 fake replacementsTest doubles

Test Doubles

• Simulate part of the system at run-time with pre-determined interactions.

• Simulations are called mocks, stubs, dummies, fake spy.

Types of Test Doubles

• Mock uses behaviour verification (has a method been called?) [3].

• Fake object has working implementation by shortcut so not useful for production (in memory db) [2]

• Stub uses state verification (is the variable equal to 2?) [3].

• Spy is a stub able to record information on how it was called (how many times?) [2]

Help the cat experimenting

Implement a class against interfaces of a JPA service, then test the class and CRUD operations using a fake object, an in memory database to test the CRUD operation correctness.

Target of the experiment

• Get confidence with in memory db and test fixture definition.

• See how developing against interfaces helps writing tests and building more flexible code.

• Injection first contact. Elegant technique to have clean and readable code, versatile units avoiding old patterns.

E2 in memory db fake objectCoding Time

05 avoid test monsterAdvice Four

Mockito Magic

05 fake replacementsTest doubles

Stunt Doubles

• Trained replacement used for dangerous action sequences in movie [6].

Test Doubles

• Act as stunt doubles. They are skilled replacement of the collaborator class.

Mock Object

• Return the expected value to a SUT inside of a well known test context.

• Keep track of invocation count.

05 Mockito overviewTest doubles

Mockito

• Mock out external dependencies, interactions will be with the mock not with DB or network connection.

• Set an expectation and the mock object returns the expected value [6].

What Mockito can do

• Verify if a method has been called or no. Verify, never, times

• Throw exceptions. Test exceptional conditions (e.g. simulate DB failure).

• Consecutive calls. Call a method in a loop, provide the list of expected returned values.

• Answer and Callbacks. Return a dynamic result and not a fixed value.

• Spying objects. Spy a real object, original behavior and some mocked behavior.

Matchers

• Built-in matchers. Wildcards anyInt(), anyString(), any(java.lang.Class<T> clazz)

Help the cat experimenting

Same exercise for class CloudService, but some tests miss, add them to better improve test protection for future refactoring and documentation of class functionalities.

Target of the experiment

• Having more details about Mockito power create test doubles to test complex functionalities.

• See differences between mocks and method override about readability, conciseness and effectiveness.

E3 find bugs by testingCoding Time

06 look at the way you code differentlyAdvice Six

TDD - Test Driven Development

06 test-first vs. test-lastApproaches

Test-last (AKA code first)

• Tests are written after implementation.

• Pros. Functionalities of the unit are well understood.

• Cons.

• Focus on testing the implementation and not the interface (behaviour) of the SUT [1]:

• test tightly coupled with implementation,

• encourage (subconsciously) to select tests expected to pass successfully.

• Temptation to not write test at all especially close to deadlines.

Test-first (AKA TDD)

• Test are written before implementation.

• Pros.

• Think about the behaviour of the unit and not the implementation.

• High code coverage, only required functionalities are added.

• Controversy. TDD is dead. - Is TDD Dead? - Summary

06 something new?TDD

Bug found

1. Write a test (put issue # in the method name) to reproduce or expose the issue.

2. Make the test fails to be sure the issue is correctly reproduced.

3. Modify the code to make the test passes.

• Pros. Take an opportunity to strength the net of automatic tests.

• Test-first approach isn’t it?

06 TDD is designing an APITDD Rhythm

TDD different perspective

• TDD is about designing an API.

• Think in terms of the API of the object by testing its external behaviour and not implementation details [1]. The test will be the client of the API implemented by the SUT.

• Focus on what is really required.

• Rule: never write code without a failing test [1]

• Tests are first-class citizens. Refactor, maintain, keep them up-to-date.

06 how apply TDDTDD Rhythm

Red

GreenRefactor

Red

• Write a test w.r.t. a new functionality or requirement (clear and well-defined task) that should be implemented.

• Where to start. Typical case, the most frequently used, the GoldenPath.

• Create class (SUT) and method from non compiling test. Run it, test will fail with a clear assertion message.

Green

• Make the test green with the smallest amount of code (YAGNI) to have a simple solution (KISS).

• The test-method must test a single, clear and well-defined requirement.

Refactor

• Change code for restructuring. Significant method names, remove duplicate code, make the code readable and easy understandable.Tests are your documentation. No functionality change. (DRY)

06 TDD advantagesTDD Rhythm

Extreme Programming Practices (XP) [Agile, TDD, User Stories etc. etc.]

• All the code is tested. You have created an automatic net of tests to protect your code.

• YAGNI (You aren't gonna need it). “Always implement things when you actually need them, never when you just foresee that you need them” [Ron Jeffries]

• DTSTTCPW (Do the simplest thing that could possibly work).

Other Principles

• KISS (Keep it Simple Stupid). Kelly Johnson, lead engineer at the Lockheed Skunk Works (creators of the Lockheed U-2 and SR-71 Blackbird spy planes).

• DRY (Do Not Repeat Yourself). Refactor to have clean, readable, easy to understand code.

Help the cat with TDD

Implement a new collector FacebookCollector, but test-first approach must be used.

Target of the experiment

• Take confidence with TDD to implement some functionalities.

• See differences between test-first and test-last approach about readability and conciseness but even speed on a long-running development.

• After class implementation

• the test suite is already done no additional thinking about how and what to test

• no code inspection to create test so no subconscious lies in the test

• test behaviour and not the implementation

• code is clean, readable, short and essential

E4 first time tddCoding Time

0D Question Time

A1 Bibliography

• [1] Practical Unit Test with JUnit and Mockito - Tomek Kaczanowski

• [2] xUnit Test Patterns: Refactoring Test Code - Gerard Meszaros

• [3] Mocks are not stubs - Martin Fowler

• [4] Test Driven Development: By Example - Kent Beck

• [5] A Set Of Unit Testing Rules - Michael Feathers

• [6] Test-Driven Development with Mockito - Sujoy Acharya

• [7] Growing Object-Oriented Software, Guided by Tests - Steve Freeman and Nat Pryce

• [8] Refactoring: Improving the Design of Existing Code - Martin Fowler and Kent Beck

• [9] Extreme Programming: A gentle introduction

• [10] Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation - Jez Humble and David Farley