test doubles, mocks, and matchers - ldstech •understanding what a test double is •creating mocks...
Post on 13-Jun-2018
215 Views
Preview:
TRANSCRIPT
Test Doubles, Mocks, and Matchers
Jeremy Lund
Notes
• This is a training, NOT a presentation
• Please ask questions
• This is being recorded
• https://tech.lds.org/wiki/Java_Stack_Training
• Prerequisites
– Basic Java
– Installed LDSTech IDE (or other equivalent)
– “Unit Testing with TestNG” training
Agenda
• Understanding what a test double is
• Creating mocks using Mockito
• Better test expressions using Hamcrest Matchers
Structure of a Unit Test
Arrange Act Assert
Structure of a Unit Test
Arrange Given
Act When
Assert Then
Structure of a Unit Test
@Test
public void isValidReturnsFalseWhenNameIsEmpty() {
// Arrange / Given
Person person = new Person();
person.setName(“”);
// Act / When
person.validate();
// Assert / Then
assertFalse(person.isValid());
}
org.lds.stack.training.mocks.examples.PersonTest
Testing Collaborators
App Controller
Category Service
Person Service
Test Collaborators
@Test
public void appControllerTest() {
// Arrange / Given
CategoryService cService =
new CategoryService(categoryRepo);
PersonService pService =
new PersonService(personRepo);
AppController appController =
new AppController(cService, pService);
// Act / When
// … run the tested actions …
// Assert / Then
// … perform some assertions …
}
Test Collaborators
@Test
public void appControllerTest() {
// Arrange / Given
CategoryService cService =
new CategoryService(categoryRepo);
PersonService pService =
new PersonService(personRepo);
AppController appController =
new AppController(cService, pService);
// Act / When
// … run the tested actions …
// Assert / Then
// … perform some assertions …
}
Testing Collaborators
App Controller
Category Service
Category Repository
Person Service
Person Search Service
Person Repository
Testing Collaborators
App Controller
Category Service
Category Repository
Jdbc Template
Category Row Mapper
Person Service
Person Search Service
Entity Manager
Person Repository
Jdbc Template
Person Row Mapper
Testing Collaborators
App Controller
Category Service
Category Repository
Jdbc Template Data Source
Category Row Mapper
Person Service
Person Search Service
Entity Manager
Person Repository
Jdbc Template Data Source
Person Row Mapper
Testing Collaborators
App Controller
Category Service
Person Service
Testing Collaborators
App Controller
Category Service
Category Repository
Jdbc Template Data Source
Category Row Mapper
Person Service
Person Search Service
Entity Manager
Person Repository
Jdbc Template Data Source
Person Row Mapper
Testing Collaborators
App Controller
Category Service
Category Repository
Jdbc Template Data Source
Category Row Mapper
Person Service
Person Search Service
Entity Manager
Person Repository
Jdbc Template Data Source
Person Row Mapper
“Gum on the Ground”
Test Doubles
Benefits of Test Doubles
• Setup
• Flexibility
• Code isolation
• Introduce deterministic behavior
• Performance
Types of Test Doubles
• Test stub
• Fake object
• Test spy
• Mock object
Which Should I Use?
• “It depends”
• Ask the question: do you need the collaborator to actually DO something with the input?
• Start with Mockito, refine if necessary
Enter: Mockito
• Somewhat based off of EasyMock
• Goal: cleaner mocking in tests
– High signal, low noise
• Techically a Test Spy
Traditional Mocking Framework Steps
Expect Run Verify
Mockito Mocking Steps
Expect
(if you care) Run
Verify
(if you care)
Adding to a Stack 3.2 Project
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<!-- optional, but recommended -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
</dependency>
Adding to Other Maven Projects
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
Defining a Mock
import static org.mockito.Mockito.*;
ClassUnderTest obj = Mockito.mock(ClassUnderTest.class);
// More common to use static import
ClassUnderTest obj = mock(ClassUnderTest.class);
org.lds.stack.training.mocks.examples.GreeterTest
Mock Behavior – Expectations and Stubs
ClassUnderTest obj = mock(ClassUnderTest.class);
// By default, methods return uninitialized value
// (null, 0, 0L, false, etc.)
// Always returns 42, no matter how many times called.
when(obj.getAnswer()).thenReturn(42);
// Returns true, then false for future interactions.
when(obj.isAwesome()).thenReturn(true).thenReturn(false);
// Void methods
// (code not available for comment)
// Throwing exceptions
when(obj.getStuff()).thenThrow(new FailException());
doThrow(new OtherException()).when(obj).doStuff(); // void
Mock Verification
// Given
ClassUnderTest obj = mock(ClassUnderTest.class);
// When
// Actions under test
// Then
// NOTE: this verifies that it happened exactly once.
verify(obj).doStuff();
// Verifies that an action never happened
// Other modifiers:
// atLeastOnce(), times(n), atMost(n), atLeast(n)
verify(obj, never()).doStuff();
// Verifies that an action occurred at least once
verify(obj, atLeastOnce()).doStuff();
Argument Matchers
// Given
Translator obj = mock(Translator.class);
// When
when(obj.getGreeting(Locale.ENGLISH)).thenReturn(“Hello”);
when(obj.getGreeting(Locale.FRENCH)).thenReturn(“Bon Jour”);
// everything else returns null
when(obj.getFarewell(any(Locale.class)).thenReturn(“BRB”);
when(obj.getFarewell(Locale.ENGLISH)).thenReturn(“Goodbye”);
when(obj.getFarewell(Locale.FRENCH)).thenReturn(“Au revoir”);
Argument Matchers
• any(), any(Class<T>)
• any…(): anyInt(), anyString(), etc.
• any…Of(Class<T>): anyListOf(Class<T>)
• eq(Obj obj): this is the default
• matches(String regex)
• startsWith(String), endsWith(String)
• argThat(Matcher<T>)
• …That(Matcher<T>): intThat(Matcher<Integer>)
Lab 1
Argument Captors
Argument Captors - Syntax
Translator obj = mock(Translator.class);
// create captor
ArgumentCaptor<Locale> localeCaptor =
ArgumentCaptor.forClass(Locale.class);
// Use the captor as an argument matcher
when(obj.getGreeting(localeCaptor.capture()))
.thenReturn(“Hello”);
// … call code that uses mock …
// Get value from last call to obj.getGreeting().
Locale value = localeCaptor.getValue();
// Returns a list of all captured values, in order.
List<Locale> values = localeCaptor.getValues();
org.lds.stack.training.mocks.examples. ContactServiceTest
Test Spies
Test Spies - Syntax
Translator obj = new TranslatorImpl();
Translator spiedOn = spy(obj);
doReturn(100).when(spiedOn).getDictionarySize();
assertEquals(spiedOn.getDictionarySize(), 100);
verify(spiedOn).getDictionarySize();
org.lds.stack.training.mocks.examples.SpyTest
Annotations – Saving the Day Since 2004
• @Mock
• @Spy
• @Captor
• @InjectMocks
NOTE: Requires a call to MockitoAnnotations.initMocks(obj);
org.lds.stack.training.mocks.examples.InjectTest
Lab 2
Hamcrest Matchers
What’s a Hamcrest Matcher?
is(equalTo(contact))
containsString(“mock”)
either(startsWith(“Test”)).or(startsWith(“Stage”))
hasPropertyWith(“address”, equalTo(“123 Fake St”))
samePropertyValuesAs(contact);
What Can I Do With Them?
assertThat();
org.lds.stack.training.mocks.examples. HamcrestAssertThatTest
Wait! There’s More!
argThat()
org.lds.stack.training.mocks.examples. HamcrestMockitoTest
Mockito Limitations
• Similar to any library that needs to dynamically extend or manipulate Java classes (think Spring AOP, libraries that use CGlib, etc.)
• Can’t mock final classes
• Can’t mock static methods
• Can’t mock final methods
• Can’t mock hashcode() or equals() (why would you?)
PowerMock Bends the Rules
PowerMock – Mocking Static Methods
@PrepareForTest(Collections.class)
public class StaticTest {
@Test
public void testStatic() {
// Tell PowerMock to mock the static methods
PowerMockito.mockStatic(Collections.class);
// Introduce mocked behavior
Mockito.when(Collections.emptyList())
.thenReturn(Arrays.<Object>asList("1", "2"));
// Try it out!
System.out.println(Collections.emptyList());
PowerMockito.verifyStatic();
org.lds.stack.training.mocks.examples.powermock.CollectionsTest
PowerMock – Mocking Finals
@PrepareForTest(FinalCountdown.class)
public class FinalTest {
@Test
public void testFinal() {
FinalCountdown finalCountdown =
PowerMockito.mock(FinalCountdown.class);
Mockito.when(finalCountdown.countdown())
.thenReturn(true, true, true, false);
Mockito.when(finalCountdown.getCurrentValue())
.thenReturn(3, 2, 1, 0);
while (finalCountdown.countdown()) {
System.out.println(
finalCountdown.getCurrentValue());
}
}
} org.lds.stack.training.mocks.examples.powermock.FinalCountdownTest
PowerMock – Mocking Private Methods
@PrepareForTest(PrivatePublic.class)
public class PrivateTest {
@Test
public void testPrivate() {
PrivatePublic privatePublic =
PowerMockito.spy(new PrivatePublic("Private"));
PowerMockito.when(privatePublic,"formatTitle")
.thenReturn("Five-Star General");
System.out.println(privatePublic.getTitle());
}
}
org.lds.stack.training.mocks.examples.powermock.PrivatePublicTest
Tips and Tricks
Don’t Mock Domain Objects
Contact contact = mock(Contact.class);
when(contact.getId()).thenReturn(15L);
when(contact.getFirstName()).thenReturn(“Chuck”);
when(contact.getLastName()).thenReturn(“Testa”);
when(contact.getStreetAddress()).thenReturn(“123 Fake St”);
when(contact.getCity()).thenReturn(“Ojai”);
when(contact.getState()).thenReturn(“CA”);
// when(does.thisEverEnd()).thenReturn(“Hooray!”);
org.lds.stack.training.mocks.examples.ContactRepositoryTest
Inject Your Collaborators
@Service
public class SuperDuperService {
private SuperService superService;
private DuperService duperService;
@Inject
public SuperDuperService(
SuperService superService,
DuperService duperService) {
this.superService = superService;
this.duperService = duperService;
}
// provide some super-duper service!
}
org.lds.stack.training.mocks.examples.SuperDuperService
Provide Reasonable Defaults
@Service
public class SuperDuperService {
private SuperService superService;
private DuperService duperService =
new DefaultDuperService();
@Inject
public SuperDuperService(SuperService superService) {
this.superService = superService;
}
public void setDuperService(DuperService duperService) {
this.duperService = duperService;
}
}
org.lds.stack.training.mocks.examples.AnotherSuperDuperService
Consider Testing When Building
What’s Next?
• Integration testing
• Test data builders
Creative Commons
Name Author URL
Lab icon 2 pitr http://openclipart.org/detail/22628/lab-icon-2-by-pitr/
Gum Alley Bev Sykes http://www.flickr.com/photos/basykes/713314598/
nuclear explosion tzunghaor http://openclipart.org/detail/166696/nuclear-explosion-by-tzunghaor
Reflections meddygarnet http://www.flickr.com/photos/meddygarnet/3457266724/
Caged Kids Morten Liebach http://www.flickr.com/photos/morten_liebach/4488573473/
FBI Dude elkbuntu http://openclipart.org/detail/10717/fbi-dude-by-elkbuntu
Creative Commons
Name Author URL
365 Day 337 Collin Harvey http://www.flickr.com/photos/celloc/6913920367/
You were the chosen one!
Kyknoord http://www.flickr.com/photos/kyknoord/2092369873/
Crash Test Dummies working on Smart Car
a200/a77Wells http://www.flickr.com/photos/aiwells/4672742619/
Wolf Memory Game Evelyn Saenz http://www.flickr.com/photos/evelynsaenz/6716600387/
top related