Testing made sweet with a Mockito

5f57d2d205e77e185986459c1b89a874?s=47 Jeroen Mols
November 11, 2016

Testing made sweet with a Mockito

Slides from my talk at Devoxx Belgium 2016
video: https://www.youtube.com/shared?ci=gkLiuk-z61A

Thanks to many tools and libraries, writing unit tests is a breeze! So all your apps currently have >80% code coverage, right? Yay! Or wait... is it really? Is all your common logic (networking, databases,...) tested? Do you isolate parts of your code base to keep your tests small? And what about providing relevant testing data?

In order to achieve this, you need to make use of mocks and stubs. And that's exactly what this talk will be about: What is a mock? What's the difference between a mock and a stub? Are mocks the only way to provide relevant data for your unit tests? What do I do with final classes/methods in the SDKs I use? How do I architect my app to make it easier to test? ...

After having adopted TDD as my main development workflow for almost a year now, I feel comfortable saying everything can be tested. Its just a matter of having someone experience show you how.

5f57d2d205e77e185986459c1b89a874?s=128

Jeroen Mols

November 11, 2016
Tweet

Transcript

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

    #MOCKITO
  2. @MOLSJEROEN #DEVOXX #MOCKITO

  3. @MOLSJEROEN #DEVOXX #MOCKITO IF YOU DON’T LIKE UNIT TESTING YOUR

    PRODUCT, MOST LIKELY YOUR CUSTOMERS WON’T LIKE TO TEST IT EITHER. Unknown
  4. @MOLSJEROEN #DEVOXX #MOCKITO ALWAYS WRITE A TEST TO REPRODUCE A

    BUG BEFORE YOU FIX IT Robert C. Martin*
  5. @MOLSJEROEN #DEVOXX #MOCKITO GOAL UI USER WEBSERVICE Button press Success/failure

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

    command Success/failure Update UI LOGININTERFACE RESPONSE
  7. @MOLSJEROEN #DEVOXX #MOCKITO WAY OF WORKING #DEVOXX #MOCKITO

  8. @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
  9. @MOLSJEROEN #DEVOXX #MOCKITO SOLUTION ▸ Isolate User ▸ Don’t include

    UI ▸ Replace WebService with dummy ▸ Mimic WebService behaviour ▸ => Verify interactions UI USER WEBSERVICE*
  10. @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 
 }
  11. @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*
  12. @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*
  13. @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*
  14. @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
  15. @MOLSJEROEN #DEVOXX #MOCKITO MOCKITO #DEVOXX #MOCKITO

  16. @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?
  17. @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
  18. @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"
 }
  19. @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"
 }
  20. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS #DEVOXX #MOCKITO

  21. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying

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

    interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  23. @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);
 }
  24. @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);
 }
  25. @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);
 }
  26. @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);
 }
  27. @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);
 }
  28. @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);
 }
  29. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying

    interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  30. @MOLSJEROEN #DEVOXX #MOCKITO VERIFYING INTERACTIONS @Test
 public void verifyInteractionTimes() throws

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

    Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService).logout();
 }
  32. @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()
 }
  33. @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!
  34. @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);
 }
  35. @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)
 }
  36. @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
  37. @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)
 }
  38. @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") }
  39. @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
  40. @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();
 }
  41. @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();
 }
  42. @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();
 }
  43. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying

    interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  44. @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();
 }
  45. @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();
 }
  46. @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();
 }
  47. @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();
 }
  48. @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();
 }
  49. @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<Boolean>() {
 int index = 0;
 
 @Override
 public Boolean answer(InvocationOnMock in) throws … {
 return index++ % 2 > 0;
 }
 }); 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }
  50. @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);
  51. @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);
  52. @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);
  53. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying

    interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  54. @MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - PROBLEM STATEMENT UI USER

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

    WEBSERVICE* Button press Success/failure Update UI LOGININTERFACE* RESPONSE Login command
  56. @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
  57. @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
  58. @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
  59. @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
  60. @MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS - SOLUTION UI USER WEBSERVICE*

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

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

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

    Button press Success/failure Update UI LOGININTERFACE* RESPONSE 4 Login command
  64. @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
  65. @MOLSJEROEN #DEVOXX #MOCKITO CAPTURING ARGUMENTS @Captor
 private ArgumentCaptor<Response> responseCaptor; UI

    USER WEBSERVICE* LOGININTERFACE* RESPONSE 0
  66. @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
  67. @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
  68. @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
  69. @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
  70. @MOLSJEROEN #DEVOXX #MOCKITO INGREDIENTS ▸ Creating a mock ▸ Verifying

    interactions ▸ Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  71. @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()
  72. @MOLSJEROEN #DEVOXX #MOCKITO ADVANCED MOCKITO #DEVOXX #MOCKITO

  73. @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
  74. @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
  75. @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
  76. @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
  77. @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'
 }
  78. @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'
 }
  79. @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'
 }
  80. @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
  81. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class

    ListContains<T> implements ArgumentMatcher<List> {
 
 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]";
 }
 }
  82. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class

    ListContains<T> implements ArgumentMatcher<List> {
 
 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]";
 }
 }
  83. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class

    ListContains<T> implements ArgumentMatcher<List> {
 
 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]";
 }
 }
  84. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class

    ListContains<T> implements ArgumentMatcher<List> {
 
 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]";
 }
 }
  85. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - LIST CONTAINS public class

    ListContains<T> implements ArgumentMatcher<List> {
 
 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]";
 }
 }
  86. @MOLSJEROEN #DEVOXX #MOCKITO CUSTOM MATCHER - STATIC METHOD public class

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

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

    ListMatchers {
 
 @Nullable
 public static <K> List listContains(K object) {
 return argThat(new ListContains<>(object));
 }
 }
  89. @MOLSJEROEN #DEVOXX #MOCKITO TRADITIONAL TEST - ARGUMENTCAPTOR @Test public void

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

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

    argumentCaptorTest() throws Exception {
 User user = new User(mockWebService, USER_ID, PASSWORD);
 ArgumentCaptor<List<String>> listCaptor = ...;
 String expectedMessage = "Test message";
 
 user.sendMessage(expectedMessage);
 
 verify(mockWebService).sendMessages(listCaptor.capture());
 List<String> messages = listCaptor.getValue();
 String actualMessage = messages.get(0);
 
 assertEquals(expectedMessage, actualMessage);
 }
  92. @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));
 }
  93. @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
  94. @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
  95. @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
 }
 }
  96. @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
 }
 }
  97. @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);
 }
 }
  98. @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);
 }
 }
  99. @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);
 }
 }
  100. @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
  101. @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();
 }
 }
  102. @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();
 }
 }
  103. @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
  104. @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
 }
  105. @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";
 } 
 ... }
  106. @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";
 } 
 ... }
  107. @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
  108. @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
  109. @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
  110. @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;
 }
  111. @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();
 }
  112. @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
  113. @MOLSJEROEN #DEVOXX #MOCKITO IF YOU’RE NOT WRITING TESTS, YOU’RE WRITING

    INSTANT LEGACY. Michael feathers*
  114. @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”
  115. @MOLSJEROEN #DEVOXX #MOCKITO WRAP UP #DEVOXX #MOCKITO

  116. @MOLSJEROEN #DEVOXX #MOCKITO IT IS BETTER TO HAVE A SIMPLE

    TEST THAT WORKS THAN A COMPLICATED TEST THAT SEEMS TO WORK. Mockito documentation
  117. @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
  118. @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
  119. @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/
  120. @MOLSJEROEN #DEVOXX #MOCKITO MANY THANKS ▸ Jeroen Mols (Belgium) ▸

    @MolsJeroen ▸ http://jeroenmols.com/blog