SlideShare a Scribd company logo
Advance Unit
Testing
Or How I Learned to Stop Worrying
and Love the unit-test
Why?
▪ Unit-test is the first client of your
code
▪ Verification and regression through
automation
▪ Powerful design tool
▪ Higher quality code
▪ Any many more…
2
Test Doubles
▪ Dummies
▪ Fakes
▪ Mock
▪ Stubs
Different types of mocks
▪ Pure mock vs nice mock
▪ Partial mocks and spies
3
Different type of tests
State Verification
▪ Execute one or many methods
of objects then assert expected
results.
▪ They keep continuing to pass
even if the internal of the method
changes.
Behavior Verification
▪ How the system behaves as it
operates rather than specifying
the expected end state.
▪ It verifies interaction between
the objects.
4
Example
public class InvitationService {
private final EmailService emailService;
private final EmailFilterService redList;
public InvitationService(EmailService
emailService, EmailFilterService redList) {
this.emailService = emailService;
this.redList = redList;
}
public void sendInvitation(Invitation invitation)
throws InviteeIsRedListed {
if (redList.redlisted(invitation.emailAddress)) {
throw new InviteeIsRedListed();
}
emailService.sendEmailInvite(invitation);
invitation.setIsSent(true);
}
}
5
State Verification Sample
@Test
public void testSendInvitation() throws InviteesRedListed {
InvitationService sut = new InvitationService(emailService,
emailFilterService);
Invitation invitation = new Invitation();
invitation.emailAddress = "1@mail.com";
sut.sendInvitation(invitation);
Assert.assertTrue(invitation.isSent());
}
@Test(expected = InviteesRedListed.class)
public void testSendInvitationGettingFiltered()
throws InviteesRedListed {
InvitationService sut = new InvitationService(emailService,
emailFilterService);
Invitation invitation = new Invitation();
invitation.emailAddress = "1@fake.com";
sut.sendInvitation(invitation);
} 6
Behavior Verification Sample
@Test
public void testSendInvitation() throws InviteesRedListed {
EmailService emailServiceMock = mock(EmailService.class);
EmailFilterService filterServiceMock = mock(EmailFilterService.class);
InvitationService sut = new InvitationService(emailServiceMock,
filterServiceMock);
Invitation invitation = new Invitation();
invitation.emailAddress = "1@test.com";
sut.sendInvitation(invitation);
// this is not important, if it is set by mock method then we can skip it.
Assert.assertTrue(invitation.isSent());
InOrder inOrder = inOrder(emailServiceMock, filterServiceMock);
inOrder.verify(filterServiceMock, times(1)).redlisted("1@test.com");
inOrder.verify(emailServiceMock, times(1)).sendEmailInvite(invitation);
}
7
Common fallacies of writing testable code
▪ Object construction in application logic
▪ Looking for things instead of asking for
them
▪ Doing too much work in the constructor
▪ Keeping global state
▪ Singleton pattern
▪ Static methods and utility classes
8
Example
public class ProfileService {
ProfileDataLayer dataLayer = new ProfileDataLayer();
ProfileValidator validator;
public ProfileService () {
validator = ProfileValidator.getValidator();
}
public CreateStatus create(String name, String imageUrl) {
try {
Profile profile = ProfileTransformer.transform(name, imageUrl);
validator.validate(profile);
dataLayer.insert(profile);
return CreateStatus.SUCCEED;
} catch (Exception e) {
return CreateStatus.FAILED;
}
}
Object construction
Looking for object
Static method call
9
Fixed example
public class ProfileService {
final ProfileDataLayer dataLayer;
final ProfileValidator validator;
final ProfileTransformer transformer
@Inject
public ProfileService (
ProfileDataLayer dataLayer,
ProfileValidator validator,
ProfileTransformer transformer) {
this.validator = validator;
this.dataLayer = dataLayer;
this.transformer = transformer;
}
Objects passed in
the constructor
through dependency
injection framework
10
Fixed example
public CreateStatus create(String name, String imageUrl) {
try {
Profile profile = transformer.transform(name, imageUrl);
validator.validate(profile);
dataLayer.insert(profile);
return CreateStatus.SUCCEED;
} catch (Exception e) {
return CreateStatus.FAILED;
}
}
No knowledge and
dependency on
implementation and
where logic comes
from.
11
Common fallacies of writing testable code
▪ Overusing inheritance as a method of code
reuse
▪ Lots of conditionals (IF and SWITCH
statements)
▪ Mixing value objects and service objects
▪ Breaking SRP (Single Responsibility
Principle)
12
Solutions overview
▪ Use dependency injection frameworks
▪ Favor composition over inheritance
▪ Favor polymorphism over conditions
▪ Command pattern, strategy patter, chain of
responsibility, etc
▪ Respect SRP
▪ RedFlag#1: Classes with bad postfix (Util,
Helper, Manager)
▪ RedFlag#2: Classes with more than 250 lines of
codes.
▪ RedFlag#3: Classes that have “And” or “OR” in
their names.
▪ RedFlag#4: Low Cohesion
▪ Validators, Transformation, Formatting, etc
should be in its own class.
13
Ultimate solution
▪ Test Driven Development - TDD
▪ Always prefer TDD for the system that you
own and you understanding the domain of
the problem very well.
▪ Test-after is OK when you are maintaining a
system or working on a horizontal feature
that you will need to change lots of other
people code.
14
Testing Legacy Code
▪ In this context Legacy code is a
code that is written and designed
without having testing in mind,
and it is still live code.
▪ They are usually hard to test.
15
Testing Legacy Code
▪ Refactoring is a powerful tool
▪ Introduce wrappers and
adapters
▪ Change access modifiers on
methods to make them visible
for testing
▪ public @interface VisibleForTesting {}
16
Writing Clean Test
17
Coding Style
Production
▪ DRY (Don’t Repeat Yourself)
▪ High cohesion.
▪ Methods are interacting with
each other
▪ Lifecycle at class level
Unit Test
▪ DAMP (Descriptive And
Meaningful Phrases)
▪ Low cohesion
▪ Each test (method) are self-
contained
▪ Lifecycle at test methods.
▪ Meaningful and long method
names.
18
DAMP vs DRY
DRY example:
public void test1() {
setupTestFixture(ps1, ps2, ps3);
setupMocks(pm1, pm2);
verify(pv1, pv2, pv3);
}
▪ Not readable in its entirety.
19
Clean tests
▪ Avoid testing several things in one test
▪ When you need to persist data for your test,
persist in-memory
▪ Setup and cleanup your test
▪ Test must run fast. Really fast!
▪ No sleeping thread
20
Clean tests (part 2)
▪ Make your test have a Cyclomatic
Complexity of one. Tests should not have
indention or indirections.
▪ Beware of Liar anti-pattern, always observe
test failures
▪ Do not use try-catch blocks in your tests
▪ Do not be a mock-happy tester
▪ Do not target high code coverage
21
Test life cycle, threading and
frameworks
▪ Tests’ life cycle is separate from
applications
▪ For example TestNG runs Suite,
Group, Class, Test, Method
configurations before and after tests
▪ Tests have different threading models
▪ Parallel forks vs single threaded
▪ Frameworks do a lot of legwork
▪ Exception and failure handling
▪ Reports
▪ Tooling: Assertions, data providers,
factories, dependencies
22
Be mindful of scopes
@Mock Map<String, Integer> counters;
@BeforeTest
public void setUp() { MockitoAnnotations.initMocks(this); }
@Test public void testCount1() {
Mockito.when(counters.get(TEST_KEY)).thenReturn(1);
Assert.assertEquals((int) counters.get(TEST_KEY), 1);
Mockito.verify(counters).get(TEST_KEY);
}
@Test public void testCount2() {
Mockito.when(counters.get(TEST_KEY)).thenReturn(2);
Assert.assertEquals((int) counters.get(TEST_KEY), 2);
Mockito.verify(counters).get(TEST_KEY);
}
Verification fails since
mock is set up for the
whole test.
(use e.g. BeforeMethod)
23
Unit Test Design Patterns
24
Can we write a test that is
both DAMP and DRY?
The answer is Yes!
25
Test Data Builder
public class UserProfileInputBuilder {
private String firstName;
private String lastName;
public UserProfileInputBuilder() {
this.firstName = "ftest";
this.lastName = "ltest";
...
}
public UserProfileInputBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public UserProfileInputBuilder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
...
public UserProfile build() {
return new UserProfile(this.firstName,
this.lastName);
}
}
26
How to use it
EmailValidator emailValidatorMock = Mockito.mock(EmailValidator.class);
LocalizeName localizerMock = Mockito.mock(LocalizeName.class);
ProfileService sut = new ProfileService(emailValidatorMock, localizerMock);
UserProfile invalidInput =
new UserProfileInputBuilder()
.withEmail("invalid email format")
.build();
Mockito.when(emailValidator.validate(invalidInput.getEmail()).thenReturn(false));
//Exercise system and expect InvalidInputException
//Note that create user is suppose to throw an exception when the email is invalid
//We do not need to provide noise and create locale, firstName, and lastName
//for this test case.
sut.createUser(invalidInput);
27
Test Data Providers
@DataProvider(name = "drinks")
public Object[][] getDrinks() {
return new Object[][] {
{ "Whiskey", true },
{ "Daiquiri", false }
};
}
@Test(dataProvider = "drinks")
public void testDrinkingService(
String drink,
boolean shouldPass) {
Assert.assertEquals(service.isGood(drink), shouldPass);
}
28
Q&A
Hope you enjoyed!
29

More Related Content

What's hot

Grails Spock Testing
Grails Spock TestingGrails Spock Testing
Grails Spock Testing
TO THE NEW | Technology
 
C++ Unit Test with Google Testing Framework
C++ Unit Test with Google Testing FrameworkC++ Unit Test with Google Testing Framework
C++ Unit Test with Google Testing Framework
Humberto Marchezi
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
pleeps
 
Unit testing patterns for concurrent code
Unit testing patterns for concurrent codeUnit testing patterns for concurrent code
Unit testing patterns for concurrent code
Dror Helper
 
Effective Unit Testing
Effective Unit TestingEffective Unit Testing
Effective Unit Testing
Narendra Pathai
 
Junit Recipes - Intro
Junit Recipes - IntroJunit Recipes - Intro
Junit Recipes - Intro
Will Shen
 
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Anna Shymchenko
 
Building unit tests correctly
Building unit tests correctlyBuilding unit tests correctly
Building unit tests correctly
Dror Helper
 
TDD Training
TDD TrainingTDD Training
TDD Training
Manuela Grindei
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
David Rodenas
 
TDD? Sure, but What About My Legacy Code?
TDD? Sure, but What About My Legacy Code?TDD? Sure, but What About My Legacy Code?
TDD? Sure, but What About My Legacy Code?
Rob Myers
 
Pyconie 2012
Pyconie 2012Pyconie 2012
Pyconie 2012
Yaqi Zhao
 
Junit 4.0
Junit 4.0Junit 4.0
Inheritance and-polymorphism
Inheritance and-polymorphismInheritance and-polymorphism
Inheritance and-polymorphism
Usama Malik
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
David Rodenas
 
Generating characterization tests for legacy code
Generating characterization tests for legacy codeGenerating characterization tests for legacy code
Generating characterization tests for legacy code
Jonas Follesø
 
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for EducationPythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
ECAM Brussels Engineering School
 
All about unit testing using (power) mock
All about unit testing using (power) mockAll about unit testing using (power) mock
All about unit testing using (power) mock
Pranalee Rokde
 
Power mock
Power mockPower mock
Power mock
Piyush Mittal
 
Mockito intro
Mockito introMockito intro
Mockito intro
Cristian R. Silva
 

What's hot (20)

Grails Spock Testing
Grails Spock TestingGrails Spock Testing
Grails Spock Testing
 
C++ Unit Test with Google Testing Framework
C++ Unit Test with Google Testing FrameworkC++ Unit Test with Google Testing Framework
C++ Unit Test with Google Testing Framework
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
Unit testing patterns for concurrent code
Unit testing patterns for concurrent codeUnit testing patterns for concurrent code
Unit testing patterns for concurrent code
 
Effective Unit Testing
Effective Unit TestingEffective Unit Testing
Effective Unit Testing
 
Junit Recipes - Intro
Junit Recipes - IntroJunit Recipes - Intro
Junit Recipes - Intro
 
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
Дмитрий Контрерас «Back to the future: the evolution of the Java Type System»
 
Building unit tests correctly
Building unit tests correctlyBuilding unit tests correctly
Building unit tests correctly
 
TDD Training
TDD TrainingTDD Training
TDD Training
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
 
TDD? Sure, but What About My Legacy Code?
TDD? Sure, but What About My Legacy Code?TDD? Sure, but What About My Legacy Code?
TDD? Sure, but What About My Legacy Code?
 
Pyconie 2012
Pyconie 2012Pyconie 2012
Pyconie 2012
 
Junit 4.0
Junit 4.0Junit 4.0
Junit 4.0
 
Inheritance and-polymorphism
Inheritance and-polymorphismInheritance and-polymorphism
Inheritance and-polymorphism
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
 
Generating characterization tests for legacy code
Generating characterization tests for legacy codeGenerating characterization tests for legacy code
Generating characterization tests for legacy code
 
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for EducationPythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
Pythia Reloaded: An Intelligent Unit Testing-Based Code Grader for Education
 
All about unit testing using (power) mock
All about unit testing using (power) mockAll about unit testing using (power) mock
All about unit testing using (power) mock
 
Power mock
Power mockPower mock
Power mock
 
Mockito intro
Mockito introMockito intro
Mockito intro
 

Similar to Advance unittest

Testing the Untestable
Testing the UntestableTesting the Untestable
Testing the Untestable
Mark Baker
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
FalafelSoftware
 
Developer Test - Things to Know
Developer Test - Things to KnowDeveloper Test - Things to Know
Developer Test - Things to Know
vilniusjug
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)
vilniusjug
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
Test driven development
Test driven developmentTest driven development
Test driven development
christoforosnalmpantis
 
VT.NET 20160411: An Intro to Test Driven Development (TDD)
VT.NET 20160411: An Intro to Test Driven Development (TDD)VT.NET 20160411: An Intro to Test Driven Development (TDD)
VT.NET 20160411: An Intro to Test Driven Development (TDD)
Rob Hale
 
The Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable DesignThe Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable Design
Victor Rentea
 
We Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End DevelopmentWe Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End Development
All Things Open
 
Shift-Left Testing: QA in a DevOps World by David Laulusa
Shift-Left Testing: QA in a DevOps World by David LaulusaShift-Left Testing: QA in a DevOps World by David Laulusa
Shift-Left Testing: QA in a DevOps World by David Laulusa
QA or the Highway
 
Unit testing
Unit testingUnit testing
Unit testing
Panos Pnevmatikatos
 
Performance Test Driven Development with Oracle Coherence
Performance Test Driven Development with Oracle CoherencePerformance Test Driven Development with Oracle Coherence
Performance Test Driven Development with Oracle Coherence
aragozin
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
Stanislav Tiurikov
 
Unit testing
Unit testingUnit testing
Unit testing
Pooya Sagharchiha
 
Test and Behaviour Driven Development (TDD/BDD)
Test and Behaviour Driven Development (TDD/BDD)Test and Behaviour Driven Development (TDD/BDD)
Test and Behaviour Driven Development (TDD/BDD)
Lars Thorup
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1
Yi-Huan Chan
 
Building unit tests correctly with visual studio 2013
Building unit tests correctly with visual studio 2013Building unit tests correctly with visual studio 2013
Building unit tests correctly with visual studio 2013
Dror Helper
 
Into The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applicationsInto The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applications
Ortus Solutions, Corp
 
Core Java Programming Language (JSE) : Chapter VI - Class Design
Core Java Programming Language (JSE) : Chapter VI - Class DesignCore Java Programming Language (JSE) : Chapter VI - Class Design
Core Java Programming Language (JSE) : Chapter VI - Class Design
WebStackAcademy
 

Similar to Advance unittest (20)

Testing the Untestable
Testing the UntestableTesting the Untestable
Testing the Untestable
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
 
Developer Test - Things to Know
Developer Test - Things to KnowDeveloper Test - Things to Know
Developer Test - Things to Know
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
 
Test driven development
Test driven developmentTest driven development
Test driven development
 
VT.NET 20160411: An Intro to Test Driven Development (TDD)
VT.NET 20160411: An Intro to Test Driven Development (TDD)VT.NET 20160411: An Intro to Test Driven Development (TDD)
VT.NET 20160411: An Intro to Test Driven Development (TDD)
 
The Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable DesignThe Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable Design
 
We Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End DevelopmentWe Are All Testers Now: The Testing Pyramid and Front-End Development
We Are All Testers Now: The Testing Pyramid and Front-End Development
 
Shift-Left Testing: QA in a DevOps World by David Laulusa
Shift-Left Testing: QA in a DevOps World by David LaulusaShift-Left Testing: QA in a DevOps World by David Laulusa
Shift-Left Testing: QA in a DevOps World by David Laulusa
 
Unit testing
Unit testingUnit testing
Unit testing
 
Performance Test Driven Development with Oracle Coherence
Performance Test Driven Development with Oracle CoherencePerformance Test Driven Development with Oracle Coherence
Performance Test Driven Development with Oracle Coherence
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
Unit testing
Unit testingUnit testing
Unit testing
 
Test and Behaviour Driven Development (TDD/BDD)
Test and Behaviour Driven Development (TDD/BDD)Test and Behaviour Driven Development (TDD/BDD)
Test and Behaviour Driven Development (TDD/BDD)
 
Test in action – week 1
Test in action – week 1Test in action – week 1
Test in action – week 1
 
Building unit tests correctly with visual studio 2013
Building unit tests correctly with visual studio 2013Building unit tests correctly with visual studio 2013
Building unit tests correctly with visual studio 2013
 
Into The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applicationsInto The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applications
 
Core Java Programming Language (JSE) : Chapter VI - Class Design
Core Java Programming Language (JSE) : Chapter VI - Class DesignCore Java Programming Language (JSE) : Chapter VI - Class Design
Core Java Programming Language (JSE) : Chapter VI - Class Design
 

Advance unittest

  • 1. Advance Unit Testing Or How I Learned to Stop Worrying and Love the unit-test
  • 2. Why? ▪ Unit-test is the first client of your code ▪ Verification and regression through automation ▪ Powerful design tool ▪ Higher quality code ▪ Any many more… 2
  • 3. Test Doubles ▪ Dummies ▪ Fakes ▪ Mock ▪ Stubs Different types of mocks ▪ Pure mock vs nice mock ▪ Partial mocks and spies 3
  • 4. Different type of tests State Verification ▪ Execute one or many methods of objects then assert expected results. ▪ They keep continuing to pass even if the internal of the method changes. Behavior Verification ▪ How the system behaves as it operates rather than specifying the expected end state. ▪ It verifies interaction between the objects. 4
  • 5. Example public class InvitationService { private final EmailService emailService; private final EmailFilterService redList; public InvitationService(EmailService emailService, EmailFilterService redList) { this.emailService = emailService; this.redList = redList; } public void sendInvitation(Invitation invitation) throws InviteeIsRedListed { if (redList.redlisted(invitation.emailAddress)) { throw new InviteeIsRedListed(); } emailService.sendEmailInvite(invitation); invitation.setIsSent(true); } } 5
  • 6. State Verification Sample @Test public void testSendInvitation() throws InviteesRedListed { InvitationService sut = new InvitationService(emailService, emailFilterService); Invitation invitation = new Invitation(); invitation.emailAddress = "1@mail.com"; sut.sendInvitation(invitation); Assert.assertTrue(invitation.isSent()); } @Test(expected = InviteesRedListed.class) public void testSendInvitationGettingFiltered() throws InviteesRedListed { InvitationService sut = new InvitationService(emailService, emailFilterService); Invitation invitation = new Invitation(); invitation.emailAddress = "1@fake.com"; sut.sendInvitation(invitation); } 6
  • 7. Behavior Verification Sample @Test public void testSendInvitation() throws InviteesRedListed { EmailService emailServiceMock = mock(EmailService.class); EmailFilterService filterServiceMock = mock(EmailFilterService.class); InvitationService sut = new InvitationService(emailServiceMock, filterServiceMock); Invitation invitation = new Invitation(); invitation.emailAddress = "1@test.com"; sut.sendInvitation(invitation); // this is not important, if it is set by mock method then we can skip it. Assert.assertTrue(invitation.isSent()); InOrder inOrder = inOrder(emailServiceMock, filterServiceMock); inOrder.verify(filterServiceMock, times(1)).redlisted("1@test.com"); inOrder.verify(emailServiceMock, times(1)).sendEmailInvite(invitation); } 7
  • 8. Common fallacies of writing testable code ▪ Object construction in application logic ▪ Looking for things instead of asking for them ▪ Doing too much work in the constructor ▪ Keeping global state ▪ Singleton pattern ▪ Static methods and utility classes 8
  • 9. Example public class ProfileService { ProfileDataLayer dataLayer = new ProfileDataLayer(); ProfileValidator validator; public ProfileService () { validator = ProfileValidator.getValidator(); } public CreateStatus create(String name, String imageUrl) { try { Profile profile = ProfileTransformer.transform(name, imageUrl); validator.validate(profile); dataLayer.insert(profile); return CreateStatus.SUCCEED; } catch (Exception e) { return CreateStatus.FAILED; } } Object construction Looking for object Static method call 9
  • 10. Fixed example public class ProfileService { final ProfileDataLayer dataLayer; final ProfileValidator validator; final ProfileTransformer transformer @Inject public ProfileService ( ProfileDataLayer dataLayer, ProfileValidator validator, ProfileTransformer transformer) { this.validator = validator; this.dataLayer = dataLayer; this.transformer = transformer; } Objects passed in the constructor through dependency injection framework 10
  • 11. Fixed example public CreateStatus create(String name, String imageUrl) { try { Profile profile = transformer.transform(name, imageUrl); validator.validate(profile); dataLayer.insert(profile); return CreateStatus.SUCCEED; } catch (Exception e) { return CreateStatus.FAILED; } } No knowledge and dependency on implementation and where logic comes from. 11
  • 12. Common fallacies of writing testable code ▪ Overusing inheritance as a method of code reuse ▪ Lots of conditionals (IF and SWITCH statements) ▪ Mixing value objects and service objects ▪ Breaking SRP (Single Responsibility Principle) 12
  • 13. Solutions overview ▪ Use dependency injection frameworks ▪ Favor composition over inheritance ▪ Favor polymorphism over conditions ▪ Command pattern, strategy patter, chain of responsibility, etc ▪ Respect SRP ▪ RedFlag#1: Classes with bad postfix (Util, Helper, Manager) ▪ RedFlag#2: Classes with more than 250 lines of codes. ▪ RedFlag#3: Classes that have “And” or “OR” in their names. ▪ RedFlag#4: Low Cohesion ▪ Validators, Transformation, Formatting, etc should be in its own class. 13
  • 14. Ultimate solution ▪ Test Driven Development - TDD ▪ Always prefer TDD for the system that you own and you understanding the domain of the problem very well. ▪ Test-after is OK when you are maintaining a system or working on a horizontal feature that you will need to change lots of other people code. 14
  • 15. Testing Legacy Code ▪ In this context Legacy code is a code that is written and designed without having testing in mind, and it is still live code. ▪ They are usually hard to test. 15
  • 16. Testing Legacy Code ▪ Refactoring is a powerful tool ▪ Introduce wrappers and adapters ▪ Change access modifiers on methods to make them visible for testing ▪ public @interface VisibleForTesting {} 16
  • 18. Coding Style Production ▪ DRY (Don’t Repeat Yourself) ▪ High cohesion. ▪ Methods are interacting with each other ▪ Lifecycle at class level Unit Test ▪ DAMP (Descriptive And Meaningful Phrases) ▪ Low cohesion ▪ Each test (method) are self- contained ▪ Lifecycle at test methods. ▪ Meaningful and long method names. 18
  • 19. DAMP vs DRY DRY example: public void test1() { setupTestFixture(ps1, ps2, ps3); setupMocks(pm1, pm2); verify(pv1, pv2, pv3); } ▪ Not readable in its entirety. 19
  • 20. Clean tests ▪ Avoid testing several things in one test ▪ When you need to persist data for your test, persist in-memory ▪ Setup and cleanup your test ▪ Test must run fast. Really fast! ▪ No sleeping thread 20
  • 21. Clean tests (part 2) ▪ Make your test have a Cyclomatic Complexity of one. Tests should not have indention or indirections. ▪ Beware of Liar anti-pattern, always observe test failures ▪ Do not use try-catch blocks in your tests ▪ Do not be a mock-happy tester ▪ Do not target high code coverage 21
  • 22. Test life cycle, threading and frameworks ▪ Tests’ life cycle is separate from applications ▪ For example TestNG runs Suite, Group, Class, Test, Method configurations before and after tests ▪ Tests have different threading models ▪ Parallel forks vs single threaded ▪ Frameworks do a lot of legwork ▪ Exception and failure handling ▪ Reports ▪ Tooling: Assertions, data providers, factories, dependencies 22
  • 23. Be mindful of scopes @Mock Map<String, Integer> counters; @BeforeTest public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testCount1() { Mockito.when(counters.get(TEST_KEY)).thenReturn(1); Assert.assertEquals((int) counters.get(TEST_KEY), 1); Mockito.verify(counters).get(TEST_KEY); } @Test public void testCount2() { Mockito.when(counters.get(TEST_KEY)).thenReturn(2); Assert.assertEquals((int) counters.get(TEST_KEY), 2); Mockito.verify(counters).get(TEST_KEY); } Verification fails since mock is set up for the whole test. (use e.g. BeforeMethod) 23
  • 24. Unit Test Design Patterns 24
  • 25. Can we write a test that is both DAMP and DRY? The answer is Yes! 25
  • 26. Test Data Builder public class UserProfileInputBuilder { private String firstName; private String lastName; public UserProfileInputBuilder() { this.firstName = "ftest"; this.lastName = "ltest"; ... } public UserProfileInputBuilder withFirstName(String firstName) { this.firstName = firstName; return this; } public UserProfileInputBuilder withLastName(String lastName) { this.lastName = lastName; return this; } ... public UserProfile build() { return new UserProfile(this.firstName, this.lastName); } } 26
  • 27. How to use it EmailValidator emailValidatorMock = Mockito.mock(EmailValidator.class); LocalizeName localizerMock = Mockito.mock(LocalizeName.class); ProfileService sut = new ProfileService(emailValidatorMock, localizerMock); UserProfile invalidInput = new UserProfileInputBuilder() .withEmail("invalid email format") .build(); Mockito.when(emailValidator.validate(invalidInput.getEmail()).thenReturn(false)); //Exercise system and expect InvalidInputException //Note that create user is suppose to throw an exception when the email is invalid //We do not need to provide noise and create locale, firstName, and lastName //for this test case. sut.createUser(invalidInput); 27
  • 28. Test Data Providers @DataProvider(name = "drinks") public Object[][] getDrinks() { return new Object[][] { { "Whiskey", true }, { "Daiquiri", false } }; } @Test(dataProvider = "drinks") public void testDrinkingService( String drink, boolean shouldPass) { Assert.assertEquals(service.isGood(drink), shouldPass); } 28