Slide 1

Slide 1 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING MADE SWEET WITH A MOCKITO #DEVOXX #MOCKITO

Slide 2

Slide 2 text

@MOLSJEROEN #DEVOXX #MOCKITO

Slide 3

Slide 3 text

@MOLSJEROEN #DEVOXX #MOCKITO IF YOU DON’T LIKE UNIT TESTING YOUR PRODUCT, MOST LIKELY YOUR CUSTOMERS WON’T LIKE TO TEST IT EITHER. Unknown

Slide 4

Slide 4 text

@MOLSJEROEN #DEVOXX #MOCKITO ALWAYS WRITE A TEST TO REPRODUCE A BUG BEFORE YOU FIX IT Robert C. Martin*

Slide 5

Slide 5 text

@MOLSJEROEN #DEVOXX #MOCKITO GOAL UI USER WEBSERVICE Button press Success/failure Update UI LOGININTERFACE RESPONSE Login/logout command

Slide 6

Slide 6 text

@MOLSJEROEN #DEVOXX #MOCKITO GOAL UI USER WEBSERVICE Button press Login/logout command Success/failure Update UI LOGININTERFACE RESPONSE

Slide 7

Slide 7 text

@MOLSJEROEN #DEVOXX #MOCKITO WAY OF WORKING #DEVOXX #MOCKITO

Slide 8

Slide 8 text

@MOLSJEROEN #DEVOXX #MOCKITO TEST REQUIREMENTS ▸ All tests must 1. Run really fast 2. Be small and focussed 3. Reliable, not “flaky” ▸ Challenges: UI & WebService UI USER WEBSERVICE

Slide 9

Slide 9 text

@MOLSJEROEN #DEVOXX #MOCKITO SOLUTION ▸ Isolate User ▸ Don’t include UI ▸ Replace WebService with dummy ▸ Mimic WebService behaviour ▸ => Verify interactions UI USER WEBSERVICE*

Slide 10

Slide 10 text

@MOLSJEROEN #DEVOXX #MOCKITO DEFINITION: TEST DOUBLES ▸ Replace real class ▸ Same interface ▸ Different behaviour WEBSERVICE* WEBSERVICE public void login() { 
 // NORMAL IMPL. 
 } public void login() { 
 // DO NOTHING 
 }

Slide 11

Slide 11 text

@MOLSJEROEN #DEVOXX #MOCKITO DEFINITION: TEST DOUBLES ▸ Replace real class ▸ Same interface ▸ Different behaviour WEBSERVICE public void login() { 
 // NORMAL IMPL. 
 } public void login() { 
 // DO NOTHING 
 } WEBSERVICE*

Slide 12

Slide 12 text

@MOLSJEROEN #DEVOXX #MOCKITO DEFINITION: TEST DOUBLES ▸ Replace real class ▸ Same interface ▸ Different behaviour WEBSERVICE public void login() { 
 // NORMAL IMPL. 
 } public void login() { 
 loginInterface.success(); 
 } WEBSERVICE*

Slide 13

Slide 13 text

@MOLSJEROEN #DEVOXX #MOCKITO DEFINITION: TEST DOUBLES ▸ Replace real class ▸ Same interface ▸ Different behaviour WEBSERVICE public void login() { 
 // NORMAL IMPL. 
 } public void login() { 
 throw new Exception(); 
 } WEBSERVICE*

Slide 14

Slide 14 text

@MOLSJEROEN #DEVOXX #MOCKITO DEFINITION: MOCKS AND STUBS ▸ What is a mock? ▸ Generated class ▸ Doesn’t do anything ▸ Behaviour verification ▸ What is a stub? ▸ Handwritten class ▸ Returns predefined responses ▸ State verification

Slide 15

Slide 15 text

@MOLSJEROEN #DEVOXX #MOCKITO MOCKITO #DEVOXX #MOCKITO

Slide 16

Slide 16 text

@MOLSJEROEN #DEVOXX #MOCKITO Open source mocking framework that lets you write beautiful tests with a clean & simple API ▸ Documentation on mockito.org ▸ Source code on Github ▸ MIT license WHAT IS IT?

Slide 17

Slide 17 text

@MOLSJEROEN #DEVOXX #MOCKITO WHY DRINK IT? ▸ Easy to use ▸ Very readable syntax ▸ No learning curve ▸ Annotation support ▸ Mature ▸ Large stackoverflow community ▸ Most popular Java / Android mocking framework

Slide 18

Slide 18 text

@MOLSJEROEN #DEVOXX #MOCKITO HOW TO DRINK IT? ▸ PROJECT_HOME/build.gradle buildscript {
 repositories {
 jcenter()
 }
 }
 
 ▸ MODULE_HOME/build.gradle dependencies {
 ... 
 testCompile “org.mockito:mockito-core:2.2.11"
 }

Slide 19

Slide 19 text

@MOLSJEROEN #DEVOXX #MOCKITO HOW TO DRINK IT? ▸ PROJECT_HOME/build.gradle buildscript {
 repositories {
 jcenter()
 }
 }
 
 ▸ MODULE_HOME/build.gradle dependencies {
 ... 
 testCompile “org.mockito:mockito-core:2.2.11"
 }

Slide 20

Slide 20 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS #DEVOXX #MOCKITO

Slide 21

Slide 21 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 22

Slide 22 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 23

Slide 23 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK import static org.mockito.Mockito.mock; @Test
 public void createMock() throws Exception {
 WebService mockWebService = mock(WebService.class);
 
 new User(mockWebService, 0, null);
 }

Slide 24

Slide 24 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK import static org.mockito.Mockito.mock; @Test
 public void createMock() throws Exception {
 WebService mockWebService = mock(WebService.class);
 
 new User(mockWebService, 0, null);
 }

Slide 25

Slide 25 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK import static org.mockito.Mockito.mock; @Test
 public void createMock() throws Exception {
 WebService mockWebService = mock(WebService.class);
 
 new User(mockWebService, 0, null);
 }

Slide 26

Slide 26 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK - ANNOTATION @Rule
 public MockitoRule mockitoRule = MockitoJUnit.rule();
 
 @Mock
 private WebService mockWebService; @Test
 public void createMockUsingAnnotation() throws Exception {
 new User(mockWebService, 0, null);
 }

Slide 27

Slide 27 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK - ANNOTATION @Rule
 public MockitoRule mockitoRule = MockitoJUnit.rule();
 
 @Mock
 private WebService mockWebService; @Test
 public void createMockUsingAnnotation() throws Exception {
 new User(mockWebService, 0, null);
 }

Slide 28

Slide 28 text

@MOLSJEROEN #DEVOXX #MOCKITO CREATING A MOCK - ANNOTATION @Rule
 public MockitoRule mockitoRule = MockitoJUnit.rule();
 
 @Mock
 private WebService mockWebService; @Test
 public void createMockUsingAnnotation() throws Exception {
 new User(mockWebService, 0, null);
 }

Slide 29

Slide 29 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 30

Slide 30 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS @Test
 public void verifyInteractionTimes() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService).logout();
 }

Slide 31

Slide 31 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS @Test
 public void verifyInteractionTimes() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService).logout();
 }

Slide 32

Slide 32 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - TIMES @Test
 public void verifyInteractionTimes() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService, times(1) ).logout();
 atLeast(1)
 atLeastOnce()
 atMost(1)
 only()
 never()
 }

Slide 33

Slide 33 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - TIMES @Test
 public void verifyInteractionTimes() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService, times(1) ).logout();
 atLeast(1)
 atLeastOnce()
 atMost(1)
 only()
 never()
 } ▸ Being overly restrictive makes tests brittle!

Slide 34

Slide 34 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(USER_ID);
 }

Slide 35

Slide 35 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(anyInt() ); anyString() any(Response.class)
 }

Slide 36

Slide 36 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(anyInt(), eq(PASSWORD));
 } ▸ One matcher -> all arguments need to be matchers

Slide 37

Slide 37 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(gt(0) );
 lt(10000) leq(10000)
 }

Slide 38

Slide 38 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(startsWith(“n1") );
 contains(“c3") matches("n[1-9]{1}c[1-9]{1}try") }

Slide 39

Slide 39 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - PARAMETERS @Test
 public void verifyInteractionParameters() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 
 verify(mockWebService).login(isNotNull(Response.class)) ); not(eq(0)) not(eq(“12345678")) and(gt(0), lt(1000000000))
 } ▸ Mockito.Matchers ▸ Mockito.AdditionalMatchers

Slide 40

Slide 40 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - ORDER @Test
 public void verifyInteractionOrder() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 user.logout();
 
 InOrder inOrder = inOrder(mockWebService);
 inOrder.verify(mockWebService).login();
 inOrder.verify(mockWebService).logout();
 }

Slide 41

Slide 41 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - ORDER @Test
 public void verifyInteractionOrder() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 user.logout();
 
 InOrder inOrder = inOrder(mockWebService);
 inOrder.verify(mockWebService).login();
 inOrder.verify(mockWebService).logout();
 }

Slide 42

Slide 42 text

@MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS - ORDER @Test
 public void verifyInteractionOrder() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.login(null);
 user.logout();
 
 InOrder inOrder = inOrder(mockWebService);
 inOrder.verify(mockWebService).login();
 inOrder.verify(mockWebService).logout();
 }

Slide 43

Slide 43 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 44

Slide 44 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenReturn(true);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 45

Slide 45 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenReturn(true);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 46

Slide 46 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenReturn(true);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 47

Slide 47 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenReturn(true, false, true);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 48

Slide 48 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenThrow(MyException.class);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 49

Slide 49 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS @Test
 public void stubMethod() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).then(new Answer() {
 int index = 0;
 
 @Override
 public Boolean answer(InvocationOnMock in) throws … {
 return index++ % 2 > 0;
 }
 }); 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }

Slide 50

Slide 50 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS ▸ Normal syntax when(mockWebService.isOffline()).thenReturn(true); 
 ▸ Alternative syntax doReturn(true).when(mockWebService).isOffline(); 
 ▸ BDD syntax given(mockWebService.isOffline()).willReturn(true);

Slide 51

Slide 51 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS ▸ Normal syntax when(mockWebService.isOffline()).thenReturn(true); 
 ▸ Alternative syntax doReturn(true).when(mockWebService).isOffline(); 
 ▸ BDD syntax given(mockWebService.isOffline()).willReturn(true);

Slide 52

Slide 52 text

@MOLSJEROEN #DEVOXX #MOCKITO STUBBING METHODS ▸ Normal syntax when(mockWebService.isOffline()).thenReturn(true); 
 ▸ Alternative syntax doReturn(true).when(mockWebService).isOffline(); 
 ▸ BDD syntax given(mockWebService.isOffline()).willReturn(true);

Slide 53

Slide 53 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 54

Slide 54 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - PROBLEM STATEMENT UI USER WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* RESPONSE Login command

Slide 55

Slide 55 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - PROBLEM STATEMENT UI USER WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* RESPONSE Login command

Slide 56

Slide 56 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - CHALLENGE public void login(final LoginInterface loginInterface) { Response response = new Response() {
 @Override
 public void onRequestCompleted(…) {
 ... } }
 webService.login(userId, password, response);
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE

Slide 57

Slide 57 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - CHALLENGE public void login(final LoginInterface loginInterface) { Response response = new Response() {
 @Override
 public void onRequestCompleted(…) {
 ... } }
 webService.login(userId, password, response);
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE

Slide 58

Slide 58 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - PROBLEM STATEMENT public class Response { 
 public void onRequestCompleted(boolean success, String data){
 if (success) {
 loginInterface.onLoginSuccess();
 } else {
 loginInterface.onLoginFailed();
 }
 } } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE

Slide 59

Slide 59 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - PROBLEM STATEMENT public class Response { 
 public void onRequestCompleted(boolean success, String data){
 if (success) {
 loginInterface.onLoginSuccess();
 } else {
 loginInterface.onLoginFailed();
 }
 } } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE

Slide 60

Slide 60 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - SOLUTION UI USER WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* RESPONSE 1 Login command

Slide 61

Slide 61 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - SOLUTION UI USER WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* 2 Login command Capture response RESPONSE

Slide 62

Slide 62 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - SOLUTION UI USER Button press Success/failure Update UI 3 WEBSERVICE* LOGININTERFACE* RESPONSE Login command

Slide 63

Slide 63 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - SOLUTION UI USER WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* RESPONSE 4 Login command

Slide 64

Slide 64 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseCaptor.capture());
 Response response = responseCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE

Slide 65

Slide 65 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Captor
 private ArgumentCaptor responseCaptor; UI USER WEBSERVICE* LOGININTERFACE* RESPONSE 0

Slide 66

Slide 66 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseCaptor.capture());
 Response response = responseCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE 1

Slide 67

Slide 67 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseCaptor.capture());
 Response response = responseCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE 2

Slide 68

Slide 68 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseCaptor.capture());
 Response response = responseCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE 3

Slide 69

Slide 69 text

@MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseCaptor.capture());
 Response response = responseCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE* LOGININTERFACE* RESPONSE 4

Slide 70

Slide 70 text

@MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations

Slide 71

Slide 71 text

@MOLSJEROEN #DEVOXX #MOCKITO MOCKITO LIMITATIONS ▸ Unable to mock ▸ final classes => opt-in with Mockito 2.x ▸ final methods => opt-in with Mockito 2.x ▸ static methods ▸ private methods ▸ hashCode() and equals()

Slide 72

Slide 72 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO #DEVOXX #MOCKITO

Slide 73

Slide 73 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 74

Slide 74 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 75

Slide 75 text

@MOLSJEROEN #DEVOXX #MOCKITO ▸ Model View Presenter ▸ Mock view & model ▸ Fully test presenter ▸ UI == “pass through” ▸ Dependency injection ▸ Only “inward pointing” dependencies TESTING UI MODEL VIEW PRESENTER VIEW IMPLEMENTATION

Slide 76

Slide 76 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 77

Slide 77 text

@MOLSJEROEN #DEVOXX #MOCKITO RUNNING MOCKITO ON AN ANDROID DEVICE ▸ MODULE_HOME/build.gradle defaultConfig {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.5'
 androidTestCompile 'com.android.support.test:rules:0.5'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker-dx:1.4' androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.4'
 }

Slide 78

Slide 78 text

@MOLSJEROEN #DEVOXX #MOCKITO RUNNING MOCKITO ON AN ANDROID DEVICE ▸ MODULE_HOME/build.gradle defaultConfig {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.5'
 androidTestCompile 'com.android.support.test:rules:0.5'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker-dx:1.4' androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.4'
 }

Slide 79

Slide 79 text

@MOLSJEROEN #DEVOXX #MOCKITO RUNNING MOCKITO ON AN ANDROID DEVICE ▸ MODULE_HOME/build.gradle defaultConfig {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.5'
 androidTestCompile 'com.android.support.test:rules:0.5'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4'
 androidTestCompile 'com.crittercism.dexmaker:dexmaker-dx:1.4' androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.4'
 }

Slide 80

Slide 80 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 81

Slide 81 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class ListContains implements ArgumentMatcher {
 
 private final T object;
 
 public ListContains(T object) {
 this.object = object;
 }
 
 public boolean matches(List list) {
 return list.contains(object);
 }
 
 public String toString() {
 //printed in verification errors
 return "[list doesn't contain object]";
 }
 }

Slide 82

Slide 82 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class ListContains implements ArgumentMatcher {
 
 private final T object;
 
 public ListContains(T object) {
 this.object = object;
 }
 
 public boolean matches(List list) {
 return list.contains(object);
 }
 
 public String toString() {
 //printed in verification errors
 return "[list doesn't contain object]";
 }
 }

Slide 83

Slide 83 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class ListContains implements ArgumentMatcher {
 
 private final T object;
 
 public ListContains(T object) {
 this.object = object;
 }
 
 public boolean matches(List list) {
 return list.contains(object);
 }
 
 public String toString() {
 //printed in verification errors
 return "[list doesn't contain object]";
 }
 }

Slide 84

Slide 84 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class ListContains implements ArgumentMatcher {
 
 private final T object;
 
 public ListContains(T object) {
 this.object = object;
 }
 
 public boolean matches(List list) {
 return list.contains(object);
 }
 
 public String toString() {
 //printed in verification errors
 return "[list doesn't contain object]";
 }
 }

Slide 85

Slide 85 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class ListContains implements ArgumentMatcher {
 
 private final T object;
 
 public ListContains(T object) {
 this.object = object;
 }
 
 public boolean matches(List list) {
 return list.contains(object);
 }
 
 public String toString() {
 //printed in verification errors
 return "[list doesn't contain object]";
 }
 }

Slide 86

Slide 86 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - STATIC METHOD public class ListMatchers {
 
 @Nullable
 public static List listContains(K object) {
 return argThat(new ListContains<>(object));
 }
 }

Slide 87

Slide 87 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - STATIC METHOD public class ListMatchers {
 
 @Nullable
 public static List listContains(K object) {
 return argThat(new ListContains<>(object));
 }
 }

Slide 88

Slide 88 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - STATIC METHOD public class ListMatchers {
 
 @Nullable
 public static List listContains(K object) {
 return argThat(new ListContains<>(object));
 }
 }

Slide 89

Slide 89 text

@MOLSJEROEN #DEVOXX #MOCKITO TRADITIONAL TEST - ARGUMENTCAPTOR @Test public void argumentCaptorTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 ArgumentCaptor> listCaptor = ...;
 String expectedMessage = "Test message";
 
 user.sendMessage(expectedMessage);
 
 verify(mockWebService).sendMessages(listCaptor.capture());
 List messages = listCaptor.getValue();
 String actualMessage = messages.get(0);
 
 assertEquals(expectedMessage, actualMessage);
 }

Slide 90

Slide 90 text

@MOLSJEROEN #DEVOXX #MOCKITO TRADITIONAL TEST - ARGUMENTCAPTOR @Test public void argumentCaptorTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 ArgumentCaptor> listCaptor = ...;
 String expectedMessage = "Test message";
 
 user.sendMessage(expectedMessage);
 
 verify(mockWebService).sendMessages(listCaptor.capture());
 List messages = listCaptor.getValue();
 String actualMessage = messages.get(0);
 
 assertEquals(expectedMessage, actualMessage);
 }

Slide 91

Slide 91 text

@MOLSJEROEN #DEVOXX #MOCKITO TRADITIONAL TEST - ARGUMENTCAPTOR @Test public void argumentCaptorTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 ArgumentCaptor> listCaptor = ...;
 String expectedMessage = "Test message";
 
 user.sendMessage(expectedMessage);
 
 verify(mockWebService).sendMessages(listCaptor.capture());
 List messages = listCaptor.getValue();
 String actualMessage = messages.get(0);
 
 assertEquals(expectedMessage, actualMessage);
 }

Slide 92

Slide 92 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER TEST @TestTest
 public void customMatcherTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 String message = "Test message";
 user.sendMessage(message);
 
 verify(mockWebService).sendMessages(listContains(message));
 }

Slide 93

Slide 93 text

@MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER TEST @TestTest
 public void customMatcherTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 String message = "Test message";
 user.sendMessage(message);
 
 verify(mockWebService).sendMessages(listContains(message));
 } RELATED BLOGPOSTS

Slide 94

Slide 94 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 95

Slide 95 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS / CLASSES public class Handler {
 
 public Handler() {};
 
 public final boolean post(Runnable r) {
 // internal implementation
 }
 
 public final boolean sendMessage(Message msg) {
 // internal implementation
 }
 }

Slide 96

Slide 96 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS / CLASSES public class Handler {
 
 public Handler() {};
 
 public final boolean post(Runnable r) {
 // internal implementation
 }
 
 public final boolean sendMessage(Message msg) {
 // internal implementation
 }
 }

Slide 97

Slide 97 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS / CLASSES public class HandlerWrapper {
 
 private final Handler handler;
 
 public HandlerWrapper() {
 handler = new Handler();
 }
 
 public boolean post(Runnable r) {
 return handler.post(r);
 }
 
 public boolean sendMessage(Message msg) {
 return handler.sendMessage(msg);
 }
 }

Slide 98

Slide 98 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS / CLASSES public class HandlerWrapper {
 
 private final Handler handler;
 
 public HandlerWrapper() {
 handler = new Handler();
 }
 
 public boolean post(Runnable r) {
 return handler.post(r);
 }
 
 public boolean sendMessage(Message msg) {
 return handler.sendMessage(msg);
 }
 }

Slide 99

Slide 99 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS / CLASSES public class HandlerWrapper {
 
 private final Handler handler;
 
 public HandlerWrapper() {
 handler = new Handler();
 }
 
 public boolean post(Runnable r) {
 return handler.post(r);
 }
 
 public boolean sendMessage(Message msg) {
 return handler.sendMessage(msg);
 }
 }

Slide 100

Slide 100 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING FINAL METHODS/CLASSES - MOCKITO 2.X ▸ Opt-in process ▸ Create new file ▸ name: org.mockito.plugins.MockMaker ▸ path: src/test/resources/mockito-extensions ▸ contents: mock-maker-inline

Slide 101

Slide 101 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING STATIC METHODS public class NativeCamera {
 
 private Camera camera = null;
 
 public Camera getNativeCamera() {
 return camera;
 }
 
 public void openNativeCamera() throws RuntimeException {
 camera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
 }
 
 public void releaseNativeCamera() {
 camera.release();
 }
 }

Slide 102

Slide 102 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING STATIC METHODS public class NativeCamera {
 
 private Camera camera = null;
 
 public Camera getNativeCamera() {
 return camera;
 }
 
 public void openNativeCamera() throws RuntimeException {
 camera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
 }
 
 public void releaseNativeCamera() {
 camera.release();
 }
 }

Slide 103

Slide 103 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 104

Slide 104 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING DATA @Test
 public void mockData() throws Exception {
 UserData mock = mock(UserData.class);
 when(mock.getFirstName()).thenReturn("FirstName");
 when(mock.getLastName()).thenReturn("LastName");
 when(mock.getUserId()).thenReturn(1111007);
 
 when(mock.getStreet()).thenReturn("StreetName");
 when(mock.getHouseNumber()).thenReturn(1);
 when(mock.getCity()).thenReturn("City");
 when(mock.getCountry()).thenReturn("Country");
 
 // Use mock in further test
 }

Slide 105

Slide 105 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING DATA @Test
 public void testData() throws Exception {
 UserData userData = new TestUserData();
 
 // use mock in further test
 } public class TestUserData extends UserData { @Override
 public String getFirstName() {
 return "FirstName";
 } 
 ... }

Slide 106

Slide 106 text

@MOLSJEROEN #DEVOXX #MOCKITO TESTING DATA @Test
 public void testData() throws Exception {
 UserData userData = new TestUserData();
 
 // use mock in further test
 } public class TestUserData extends UserData { @Override
 public String getFirstName() {
 return "FirstName";
 } 
 ... }

Slide 107

Slide 107 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 108

Slide 108 text

@MOLSJEROEN #DEVOXX #MOCKITO NEW CREATION public User(int userId, String password) {
 this.webService = new WebService();
 } ▸ Internal created objects ▸ almost impossible to test ▸ avoid for long lived objects

Slide 109

Slide 109 text

@MOLSJEROEN #DEVOXX #MOCKITO NEW CREATION public User(int userId, String password) {
 this.webService = new WebService();
 } ▸ Internal created objects ▸ almost impossible to test ▸ avoid for long lived objects

Slide 110

Slide 110 text

@MOLSJEROEN #DEVOXX #MOCKITO NEW CREATION public User(int userId, String password) {
 this.webService = new WebService();
 } ▸ Dependency injection public User(WebService webService, int user, String password) {
 if (webService == null) {
 throw new RuntimeException("Webservice required");
 }
 this.webService = webService;
 }

Slide 111

Slide 111 text

@MOLSJEROEN #DEVOXX #MOCKITO NEW CREATION public User(int userId, String password) {
 this.webService = new WebService();
 } ▸ Factory pattern public User(WebServiceFactory factory, int user, String pw) {
 this.webService = factory.createWebService();
 }

Slide 112

Slide 112 text

@MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO ▸ Testing UI ▸ Running Mockito on an Android device ▸ Custom Matchers ▸ Testing final and static ▸ Testing data ▸ New creation ▸ Power tips

Slide 113

Slide 113 text

@MOLSJEROEN #DEVOXX #MOCKITO IF YOU’RE NOT WRITING TESTS, YOU’RE WRITING INSTANT LEGACY. Michael feathers*

Slide 114

Slide 114 text

@MOLSJEROEN #DEVOXX #MOCKITO POWER TIPS ▸ Avoid singletons ▸ Avoid Android instrumentation tests ▸ Isolate non testable code ▸ Don’t use Spies ▸ Don’t nest mocks ▸ Avoid using “For testing methods”

Slide 115

Slide 115 text

@MOLSJEROEN #DEVOXX #MOCKITO WRAP UP #DEVOXX #MOCKITO

Slide 116

Slide 116 text

@MOLSJEROEN #DEVOXX #MOCKITO IT IS BETTER TO HAVE A SIMPLE TEST THAT WORKS THAN A COMPLICATED TEST THAT SEEMS TO WORK. Mockito documentation

Slide 117

Slide 117 text

@MOLSJEROEN #DEVOXX #MOCKITO CONCLUSION ▸ Mockito makes mocking & stubbing easy ▸ Avoid creating internal objects ▸ Choose a good testing strategy ▸ Don’t overuse the power features ▸ Keep unit tests small and focussed ▸ Consider refactoring ▸ Prefer tests on the java VM

Slide 118

Slide 118 text

@MOLSJEROEN #DEVOXX #MOCKITO REFERENCES ▸ Mockito homepage http://mockito.org/ ▸ Mockito reference card https://dzone.com/refcardz/mockito ▸ Dexmaker https://github.com/crittercism/dexmaker ▸ Sample code ▸ MockitoExample https://github.com/JeroenMols/MockitoExample ▸ LandscapeVideoCamera https://github.com/JeroenMols/LandscapeVideoCamera ▸ MockitoColletionMatchers https://github.com/JeroenMols/MockitoCollectionMatchers

Slide 119

Slide 119 text

@MOLSJEROEN #DEVOXX #MOCKITO IMAGE CREDITS ▸ Mojito
 https://indraneescookingcorner.blogspot.be/2014/08/virgin-mojito.html ▸ Philips Hue living room
 http://www.philips.de ▸ Bartender set
 http://www.vinspireuk.com/2014/12/cocktail-gifts-for-cocktail-lovers.html ▸ Mojito ingredients
 https://www.nelleulla.com/lv-en/products/truffles/4-mojito-truffle ▸ Mockito Mojito
 http://www.seedylawyer.com/history-of-hemingways-mojitos/ ▸ Raspberry Mojito
 http://goodpixcool.com/raspberry+mojito+in+a+bottle?image=99002602 ▸ Drinking Mojito
 https://www.lancerskincare.com/blog/10-ways-to-age-your-skin-prematurely/

Slide 120

Slide 120 text

@MOLSJEROEN #DEVOXX #MOCKITO MANY THANKS ▸ Jeroen Mols (Belgium) ▸ @MolsJeroen ▸ http://jeroenmols.com/blog