SlideShare a Scribd company logo
C l e a n U n i t T e s t P a t t e r n s 
F r a n k A p p e l Blog: www.codeaffine.com 
Email: fappel@codeaffine.com 
Twitter: @frank_appel
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Who I am.. 
Independent Software Developer 
Blogger (http://codeaffine.com/blog) 
Stalwart of agile methods and TDD in particular
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Why bother?
Why bother? 
Test of too many concepts
Why bother?
Why bother? 
Mix of integration and unit test
Why bother?
Why bother? 
Missing of clean and recognizable test structure
Why bother?
Why bother? 
Tight coupling of unit under test and dependencies
Why bother?
Why bother? 
Poor maintainability and progression
Why bother?
Why bother? 
Don't do it!
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Test Structure 
Four Phases Pattern
Test Structure 
Four Phases Pattern 
Setup (Fixture)
Test Structure 
Four Phases Pattern 
Setup (Fixture) 
Exercise
Test Structure 
Four Phases Pattern 
Setup (Fixture) 
Exercise 
Verify
Test Structure 
Four Phases Pattern 
Setup (Fixture) 
Exercise 
Verify 
Teardown: cleaning up the fixture in case it is persistent.
Test Structure 
Four Phases Pattern 
Setup (Fixture) 
Exercise 
Verify 
Teardown: cleaning up the fixture in case it is persistent.
Test Structure 
Setup Patterns
Test Structure 
Setup Patterns 
Inline Setup
Test Structure 
Setup Patterns
Test Structure 
Setup Patterns
Test Structure 
Setup Patterns 
Delegate Setup
Test Structure 
Setup Patterns
Test Structure 
Setup Patterns
Test Structure 
Setup Patterns 
Implicit Setup
Test Structure 
Reusable Setup Helper 
Object Mother 
Test Fixture Registry 
Implementation either as stateless test helper class or, in case the helper 
holds references to fixture or SUT objects, as stateful test helper object.
Test Structure 
Implicit Teardown 
Teardown is all about housekeeping and adds no information at all to a 
particular test
Test Structure 
Corner Case Tests: Expected Exceptions 
Traditional approach: ugly try-catch block, mix of phases
Test Structure 
Corner Case Tests: Expected Exceptions
Test Structure 
Corner Case Tests: Expected Exceptions 
Expected Annotation Method: might swallow setup problems, limited verification 
capabilities
Test Structure 
Corner Case Tests: Expected Exceptions
Test Structure 
Corner Case Tests: Expected Exceptions 
ExpectedException Rule: Verification definition before execution phase
Test Structure 
Corner Case Tests: Expected Exceptions
Test Structure 
Corner Case Tests: Expected Exceptions 
Usage of a little execution utility and Java 8 Lambda expressions
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Isolation 
Dependencies: SUT and DOC 
The tested Unit is usually referred to as system under test (SUT) 
Components the SUT depends on are denoted as depended-on 
component (DOC) 
Test related problems with DOCs 
DOCs we cannot control might impede decent test verification 
DOCs might also slow down test execution 
DOC’s behavior may change unexpectedly e.g. due to the usage of a 
newer version of a third party library
Isolation 
Isolation – A Unit Tester’s SEP Field 
Test concerns seperately and keep tests independent of each other! 
Indirect Inputs and Outputs
Isolation 
Test Double Patterns 
A unit should be designed in a way that each DOC can be replaced by a 
so called Test Double, which is a lightweight stand-in component for the 
DOC. 
A DOC is provided using dependency injection or a service locator. This 
ensures a loosely coupled micro-architecture that allows replacement of 
the DOC with the test double.
Isolation 
Controlling Indirect Inputs with Stubs
Isolation 
Controlling Indirect Inputs with Stubs
Isolation 
Controlling Indirect Inputs with Stubs
Isolation 
Controlling Indirect Inputs with Stubs
Isolation 
Controlling Indirect Inputs with Stubs
Isolation 
Indirect Output Verification with Spies 
The spy records the number value of the invocation in the test double’s number field. 
This allows to verify the indirect output as shown here: 
record 
verify
Isolation 
What About Mocks?
Isolation 
What About Mocks? 
Behavior Verifcation
Isolation 
What About Mocks? 
Behavior Verifcation 
Invocation Verification
Isolation 
What About Mocks?
Isolation 
What About Mocks?
Isolation 
Spy or Mock? 
Mocks break the usual test structure 
Behavior verification is somewhat hidden 
but 
Mocks provide a precise stacktrace to the failure cause
Isolation 
Test Double Frameworks 
JMock, EasyMock mock based 
Mockito spy based
Isolation 
Test Double Frameworks 
Only create test doubles for types you own 
(indication for integration tests and an abstracting adapter layer) 
A test double should not return another test double 
(potential violation of law of demeter)
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Runners 
Suite and Categories 
Use @RunWith to specify a particular test processor
Runners 
Suite and Categories
Runners 
Suite and Categories
Runners 
Parameterized Tests 
Parameterized tests allow to run the same test against multiple data records 
provided as instance field(s) of the test case 
fields
Runners 
Parameterized Tests
Runners 
Parameterized Tests 
Specification of the Parameterized test processor using @RunWith
Runners 
Parameterized Tests
Runners 
Parameterized Tests 
Field initialization takes place by constructor injection. Each data record is 
provided by a particular collector method annotated with @Parameters 
data records 
initializations
Runners 
JUnitParams
Rules 
What are JUnit Rules? 
Rules provide a possibility to intercept test method calls similar as an AOP framework 
would do. 
TemporaryFolder for example removes automatically all created files and directories 
after a test run. 
A list of JUnit built-in Rules can be found at: 
https://github.com/junit-team/junit/wiki/Rules
Rules 
How does it work? 
y
Rules 
How does it work?
Rules 
How does it work? 
Test execution produces: 
before 
during 
after
Rules 
How does it work?
Rules 
How does it work?
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
Assertions 
JUnit Assert 
The built-in assertion mechanism of JUnit is provided by the class org.junit.Assert: 
It is quite verbose and somewhat limited with respect to the expressiveness of 
assertions that require more complex predicates
Assertions 
Hamcrest 
A third-party library that claims to provide an API for creating flexible expressions of 
intent is Hamcrest: 
MatcherAssert.assertThat(...) evaluates the execution result (actual) against a 
predicate (matcher-expression) 
MatcherAssert provides an overloaded assertThat method for failure message 
specification
Assertions 
Hamcrest
Assertions 
Hamcrest
Assertions 
AssertJ 
The library AssertJ strives to improves verification by providing fluent assertions API: 
Assertions.assertThat(...) verifies the execution result (actual) against fluently added 
conditions 
The Assert instance provides the method describeAs(String) to specify a particular 
failure message
Assertions 
AssertJ
Assertions 
AssertJ
Assertions 
Which one to use? 
JUnit Assert is surely somewhat dated and less object-oriented 
Hamcrest matchers provide a clean separation of assertion 
and predicate definition 
AssertJ assertions score with a compact and easy to use programming 
style 
Hamcrest and AssertJ support custom matchers/assertions for domain 
specific types 
So now you are spoilt for choice…
C l e a n U n i t T e s t P a t t e r n s 
Why bother? 
Structure 
Isolation 
Runners and Rules 
Assertions 
Q&A
C l e a n U n i t T e s t P a t t e r n s 
References 
xUnit Test Patterns, Gerard Meszaros, 2007 
Clean Code, Chapter 9: Unit Tests, Robert C. Martin, 2009 
Growing Object-Oriented Software, Guided by Tests, Chapter 8, Steve Freeman, Nat Pryce, 2010 
Practical Unit Testing with JUnit and Mockito, Appendix C. Test Spy vs. Mock, Tomek Kaczanowski, 
2013 
JUnit in a Nutshell: Yet Another JUnit Tutorial, 
http://www.codeaffine.com/2014/09/24/junit-nutshell-junit-tutorial, Frank Appel 2014 
Clean JUnit Throwable-Tests with Java 8 Lambdas, 
http://www.codeaffine.com/2014/07/28/clean-junit-throwable-tests-with-java-8-lambdas/, 
Frank Appel 2014 
F r a n k A p p e l Blog: www.codeaffine.com 
Email: fappel@codeaffine.com 
Twitter: @frank_appel

More Related Content

PPTX
Unit Testing And Mocking
PDF
JUnit & Mockito, first steps
PPSX
PPTX
An Introduction to Unit Testing
PPTX
Unit tests & TDD
PDF
Unit testing best practices
PPTX
JUnit- A Unit Testing Framework
PPTX
Manual Testing tutorials and Interview Questions.pptx
Unit Testing And Mocking
JUnit & Mockito, first steps
An Introduction to Unit Testing
Unit tests & TDD
Unit testing best practices
JUnit- A Unit Testing Framework
Manual Testing tutorials and Interview Questions.pptx

What's hot (20)

PPTX
Unit Testing in Java
PDF
Unit and integration Testing
PPTX
Unit Testing Concepts and Best Practices
PPTX
Unit testing & TDD concepts with best practice guidelines.
PDF
Mocking in Java with Mockito
PPTX
JUNit Presentation
PDF
An introduction to unit testing
PDF
Workshop unit test
PPTX
Whitebox testing of Spring Boot applications
PDF
Java 8 Lambda Expressions & Streams
PPTX
Jenkins CI
PPT
PDF
JUnit 5 - The Next Generation
PPS
Unit Testing
PDF
Inverting The Testing Pyramid
PPTX
Java 8 presentation
PPTX
Unit Testing
PPS
JUnit Presentation
PPTX
Azure Pipelines
PDF
Configuration management II - Terraform
Unit Testing in Java
Unit and integration Testing
Unit Testing Concepts and Best Practices
Unit testing & TDD concepts with best practice guidelines.
Mocking in Java with Mockito
JUNit Presentation
An introduction to unit testing
Workshop unit test
Whitebox testing of Spring Boot applications
Java 8 Lambda Expressions & Streams
Jenkins CI
JUnit 5 - The Next Generation
Unit Testing
Inverting The Testing Pyramid
Java 8 presentation
Unit Testing
JUnit Presentation
Azure Pipelines
Configuration management II - Terraform
Ad

Viewers also liked (20)

PPTX
UNIT TESTING PPT
PPTX
Understanding Unit Testing
PPTX
Unit Testing (C#)
PPTX
Moq Presentation
PPTX
Mock driven development using .NET
PPTX
Principles and patterns for test driven development
PPTX
Intro to Mocking - DjangoCon 2015
PDF
Unit-Testing Bad-Practices by Example
PPTX
Практика использования Dependency Injection
PDF
Василий Сорокин, “Google C++ Mocking and Test Frameworks”
PDF
DI в C++ тонкости и нюансы
PPT
Юнит-тестирование и Google Mock. Влад Лосев, Google
PPT
Boundary value analysis
PPT
Mockito (JUG Latvia)
PDF
Programmer testing
KEY
Basic Unit Testing with Mockito
PDF
Software Engineering - RS3
PPTX
Demystifying git
PDF
All about unit testing using (power) mock
PDF
Павел Филонов, Разделяй и управляй вместе с Conan.io
UNIT TESTING PPT
Understanding Unit Testing
Unit Testing (C#)
Moq Presentation
Mock driven development using .NET
Principles and patterns for test driven development
Intro to Mocking - DjangoCon 2015
Unit-Testing Bad-Practices by Example
Практика использования Dependency Injection
Василий Сорокин, “Google C++ Mocking and Test Frameworks”
DI в C++ тонкости и нюансы
Юнит-тестирование и Google Mock. Влад Лосев, Google
Boundary value analysis
Mockito (JUG Latvia)
Programmer testing
Basic Unit Testing with Mockito
Software Engineering - RS3
Demystifying git
All about unit testing using (power) mock
Павел Филонов, Разделяй и управляй вместе с Conan.io
Ad

Similar to Clean Unit Test Patterns (20)

PDF
Unit testing [4] - Software Testing Techniques (CIS640)
PPT
Testing Software Engineering systems end to end
PDF
Test driven development - JUnit basics and best practices
PPTX
Unit testing
PPTX
2016 10-04: tdd++: tdd made easier
PPTX
Unit Testing and TDD 2017
PPT
Software Engineering XUnit Testing and Patterns
PPTX
TDD - Unit Testing
PDF
An Introduction to Test Driven Development
PPTX
Testing with Junit4
PPTX
PPTX
Renaissance of JUnit - Introduction to JUnit 5
PDF
The Art of Unit Testing Feedback
PPTX
L2624 labriola
PDF
junit-160729073220 eclipse software testing.pdf
PDF
Unit testing with JUnit
PDF
TDD Workshop UTN 2012
PDF
Unit Tesing in iOS
PPTX
Junit4&testng presentation
PPTX
Unit testing
Unit testing [4] - Software Testing Techniques (CIS640)
Testing Software Engineering systems end to end
Test driven development - JUnit basics and best practices
Unit testing
2016 10-04: tdd++: tdd made easier
Unit Testing and TDD 2017
Software Engineering XUnit Testing and Patterns
TDD - Unit Testing
An Introduction to Test Driven Development
Testing with Junit4
Renaissance of JUnit - Introduction to JUnit 5
The Art of Unit Testing Feedback
L2624 labriola
junit-160729073220 eclipse software testing.pdf
Unit testing with JUnit
TDD Workshop UTN 2012
Unit Tesing in iOS
Junit4&testng presentation
Unit testing

Recently uploaded (20)

PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPT
Teaching material agriculture food technology
PDF
cuic standard and advanced reporting.pdf
PDF
KodekX | Application Modernization Development
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
Spectroscopy.pptx food analysis technology
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Electronic commerce courselecture one. Pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
NewMind AI Weekly Chronicles - August'25 Week I
Teaching material agriculture food technology
cuic standard and advanced reporting.pdf
KodekX | Application Modernization Development
20250228 LYD VKU AI Blended-Learning.pptx
MYSQL Presentation for SQL database connectivity
Understanding_Digital_Forensics_Presentation.pptx
The Rise and Fall of 3GPP – Time for a Sabbatical?
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Spectroscopy.pptx food analysis technology
Chapter 3 Spatial Domain Image Processing.pdf
sap open course for s4hana steps from ECC to s4
Review of recent advances in non-invasive hemoglobin estimation
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Agricultural_Statistics_at_a_Glance_2022_0.pdf
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Digital-Transformation-Roadmap-for-Companies.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Electronic commerce courselecture one. Pdf

Clean Unit Test Patterns

  • 1. C l e a n U n i t T e s t P a t t e r n s F r a n k A p p e l Blog: www.codeaffine.com Email: fappel@codeaffine.com Twitter: @frank_appel
  • 2. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 3. Who I am.. Independent Software Developer Blogger (http://codeaffine.com/blog) Stalwart of agile methods and TDD in particular
  • 4. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 6. Why bother? Test of too many concepts
  • 8. Why bother? Mix of integration and unit test
  • 10. Why bother? Missing of clean and recognizable test structure
  • 12. Why bother? Tight coupling of unit under test and dependencies
  • 14. Why bother? Poor maintainability and progression
  • 17. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 18. Test Structure Four Phases Pattern
  • 19. Test Structure Four Phases Pattern Setup (Fixture)
  • 20. Test Structure Four Phases Pattern Setup (Fixture) Exercise
  • 21. Test Structure Four Phases Pattern Setup (Fixture) Exercise Verify
  • 22. Test Structure Four Phases Pattern Setup (Fixture) Exercise Verify Teardown: cleaning up the fixture in case it is persistent.
  • 23. Test Structure Four Phases Pattern Setup (Fixture) Exercise Verify Teardown: cleaning up the fixture in case it is persistent.
  • 25. Test Structure Setup Patterns Inline Setup
  • 28. Test Structure Setup Patterns Delegate Setup
  • 31. Test Structure Setup Patterns Implicit Setup
  • 32. Test Structure Reusable Setup Helper Object Mother Test Fixture Registry Implementation either as stateless test helper class or, in case the helper holds references to fixture or SUT objects, as stateful test helper object.
  • 33. Test Structure Implicit Teardown Teardown is all about housekeeping and adds no information at all to a particular test
  • 34. Test Structure Corner Case Tests: Expected Exceptions Traditional approach: ugly try-catch block, mix of phases
  • 35. Test Structure Corner Case Tests: Expected Exceptions
  • 36. Test Structure Corner Case Tests: Expected Exceptions Expected Annotation Method: might swallow setup problems, limited verification capabilities
  • 37. Test Structure Corner Case Tests: Expected Exceptions
  • 38. Test Structure Corner Case Tests: Expected Exceptions ExpectedException Rule: Verification definition before execution phase
  • 39. Test Structure Corner Case Tests: Expected Exceptions
  • 40. Test Structure Corner Case Tests: Expected Exceptions Usage of a little execution utility and Java 8 Lambda expressions
  • 41. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 42. Isolation Dependencies: SUT and DOC The tested Unit is usually referred to as system under test (SUT) Components the SUT depends on are denoted as depended-on component (DOC) Test related problems with DOCs DOCs we cannot control might impede decent test verification DOCs might also slow down test execution DOC’s behavior may change unexpectedly e.g. due to the usage of a newer version of a third party library
  • 43. Isolation Isolation – A Unit Tester’s SEP Field Test concerns seperately and keep tests independent of each other! Indirect Inputs and Outputs
  • 44. Isolation Test Double Patterns A unit should be designed in a way that each DOC can be replaced by a so called Test Double, which is a lightweight stand-in component for the DOC. A DOC is provided using dependency injection or a service locator. This ensures a loosely coupled micro-architecture that allows replacement of the DOC with the test double.
  • 45. Isolation Controlling Indirect Inputs with Stubs
  • 46. Isolation Controlling Indirect Inputs with Stubs
  • 47. Isolation Controlling Indirect Inputs with Stubs
  • 48. Isolation Controlling Indirect Inputs with Stubs
  • 49. Isolation Controlling Indirect Inputs with Stubs
  • 50. Isolation Indirect Output Verification with Spies The spy records the number value of the invocation in the test double’s number field. This allows to verify the indirect output as shown here: record verify
  • 52. Isolation What About Mocks? Behavior Verifcation
  • 53. Isolation What About Mocks? Behavior Verifcation Invocation Verification
  • 56. Isolation Spy or Mock? Mocks break the usual test structure Behavior verification is somewhat hidden but Mocks provide a precise stacktrace to the failure cause
  • 57. Isolation Test Double Frameworks JMock, EasyMock mock based Mockito spy based
  • 58. Isolation Test Double Frameworks Only create test doubles for types you own (indication for integration tests and an abstracting adapter layer) A test double should not return another test double (potential violation of law of demeter)
  • 59. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 60. Runners Suite and Categories Use @RunWith to specify a particular test processor
  • 61. Runners Suite and Categories
  • 62. Runners Suite and Categories
  • 63. Runners Parameterized Tests Parameterized tests allow to run the same test against multiple data records provided as instance field(s) of the test case fields
  • 65. Runners Parameterized Tests Specification of the Parameterized test processor using @RunWith
  • 67. Runners Parameterized Tests Field initialization takes place by constructor injection. Each data record is provided by a particular collector method annotated with @Parameters data records initializations
  • 69. Rules What are JUnit Rules? Rules provide a possibility to intercept test method calls similar as an AOP framework would do. TemporaryFolder for example removes automatically all created files and directories after a test run. A list of JUnit built-in Rules can be found at: https://github.com/junit-team/junit/wiki/Rules
  • 70. Rules How does it work? y
  • 71. Rules How does it work?
  • 72. Rules How does it work? Test execution produces: before during after
  • 73. Rules How does it work?
  • 74. Rules How does it work?
  • 75. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 76. Assertions JUnit Assert The built-in assertion mechanism of JUnit is provided by the class org.junit.Assert: It is quite verbose and somewhat limited with respect to the expressiveness of assertions that require more complex predicates
  • 77. Assertions Hamcrest A third-party library that claims to provide an API for creating flexible expressions of intent is Hamcrest: MatcherAssert.assertThat(...) evaluates the execution result (actual) against a predicate (matcher-expression) MatcherAssert provides an overloaded assertThat method for failure message specification
  • 80. Assertions AssertJ The library AssertJ strives to improves verification by providing fluent assertions API: Assertions.assertThat(...) verifies the execution result (actual) against fluently added conditions The Assert instance provides the method describeAs(String) to specify a particular failure message
  • 83. Assertions Which one to use? JUnit Assert is surely somewhat dated and less object-oriented Hamcrest matchers provide a clean separation of assertion and predicate definition AssertJ assertions score with a compact and easy to use programming style Hamcrest and AssertJ support custom matchers/assertions for domain specific types So now you are spoilt for choice…
  • 84. C l e a n U n i t T e s t P a t t e r n s Why bother? Structure Isolation Runners and Rules Assertions Q&A
  • 85. C l e a n U n i t T e s t P a t t e r n s References xUnit Test Patterns, Gerard Meszaros, 2007 Clean Code, Chapter 9: Unit Tests, Robert C. Martin, 2009 Growing Object-Oriented Software, Guided by Tests, Chapter 8, Steve Freeman, Nat Pryce, 2010 Practical Unit Testing with JUnit and Mockito, Appendix C. Test Spy vs. Mock, Tomek Kaczanowski, 2013 JUnit in a Nutshell: Yet Another JUnit Tutorial, http://www.codeaffine.com/2014/09/24/junit-nutshell-junit-tutorial, Frank Appel 2014 Clean JUnit Throwable-Tests with Java 8 Lambdas, http://www.codeaffine.com/2014/07/28/clean-junit-throwable-tests-with-java-8-lambdas/, Frank Appel 2014 F r a n k A p p e l Blog: www.codeaffine.com Email: fappel@codeaffine.com Twitter: @frank_appel