Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing made sweet with a Mockito

Testing made sweet with a Mockito

Slides from my talk at Droidcon Berlin and Big Android BBQ Amsterdam 2016
audio: https://voicerepublic.com/talks/testing-made-sweet-with-a-mockito

The past year has been huge for Android testing: Testing support lib, fast JVM unit tests,... Having such great tools means writing tests is a breeze! All your apps currently have >80% code coverage, right? Yay! Or wait... is it really? Is all your common Android 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 all the final classes/methods in the Android SDK? 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.

Jeroen Mols

June 16, 2016
Tweet

More Decks by Jeroen Mols

Other Decks in Programming

Transcript

  1. IF YOU DON’T LIKE UNIT TESTING YOUR PRODUCT, MOST LIKELY

    YOUR CUSTOMERS WON’T LIKE TO TEST IT EITHER. Unknown
  2. @MOLSJEROEN TEST REQUIREMENTS ▸ All tests must ▸ Run really

    fast ▸ Be small and focussed ▸ Reliable, not “flaky” ▸ Challenges: UI & WebService UI USER WEBSERVICE
  3. @MOLSJEROEN SOLUTION ▸ Isolate User ▸ Don’t include UI ▸

    Replace WebService with dummy ▸ Mimic WebService behaviour ▸ => Verify interactions UI USER WEBSERVICE*
  4. @MOLSJEROEN DEFINITION: TEST DOUBLES ▸ Replace real class ▸ Same

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

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

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

    interface ▸ Different behaviour WEBSERVICE* WEBSERVICE public void login() { 
 // NORMAL IMPL. 
 } public void login() { 
 throw new Exception(); 
 }
  8. @MOLSJEROEN 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
  9. @MOLSJEROEN 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?
  10. @MOLSJEROEN WHY DRINK IT? ▸ Easy to use ▸ Very

    readable syntax ▸ No learning curve ▸ Annotation support ▸ Mature ▸ Large stackoverflow community ▸ Most popular Java / Android mocking framework
  11. @MOLSJEROEN HOW TO DRINK IT? ▸ PROJECT_HOME/build.gradle buildscript {
 repositories

    {
 jcenter()
 }
 }
 
 ▸ MODULE_HOME/build.gradle dependencies {
 ... 
 testCompile "org.mockito:mockito-core:2.0.101-beta"
 }
  12. @MOLSJEROEN HOW TO DRINK IT? ▸ PROJECT_HOME/build.gradle buildscript {
 repositories

    {
 jcenter()
 }
 }
 
 ▸ MODULE_HOME/build.gradle dependencies {
 ... 
 testCompile "org.mockito:mockito-core:2.0.101-beta"
 }
  13. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  14. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  15. @MOLSJEROEN 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);
 }
  16. @MOLSJEROEN 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);
 }
  17. @MOLSJEROEN 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);
 }
  18. @MOLSJEROEN 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);
 }
  19. @MOLSJEROEN 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);
 }
  20. @MOLSJEROEN 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);
 }
  21. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  22. @MOLSJEROEN VERIFYING INTERACTIONS @Test
 public void verifyInteractionTimes() throws Exception {


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


    User user = new User(mockWebService, USER_ID, PASSWORD);
 
 user.logout();
 
 verify(mockWebService).logout();
 }
  24. @MOLSJEROEN 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()
 }
  25. @MOLSJEROEN 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!
  26. @MOLSJEROEN 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);
 }
  27. @MOLSJEROEN 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)
 }
  28. @MOLSJEROEN 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
  29. @MOLSJEROEN 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)
 }
  30. @MOLSJEROEN 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}[a-z]{3}") }
  31. @MOLSJEROEN 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(Integer.MAX_VALUE))
 } ▸ Mockito.Matchers ▸ Mockito.AdditionalMatchers
  32. @MOLSJEROEN 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();
 }
  33. @MOLSJEROEN 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();
 }
  34. @MOLSJEROEN 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();
 }
  35. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  36. @MOLSJEROEN 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();
 }
  37. @MOLSJEROEN 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();
 }
  38. @MOLSJEROEN 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();
 }
  39. @MOLSJEROEN 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();
 }
  40. @MOLSJEROEN STUBBING METHODS @Test
 public void stubMethod() throws Exception {


    User user = new User(mockWebService, USER_ID, PASSWORD);
 when(mockWebService.isOffline()).thenThrow(CustomException.class);
 
 user.login(mockLoginInterface);
 
 verify(mockWebService, never()).login();
 }
  41. @MOLSJEROEN STUBBING METHODS ▸ Normal syntax when(mockWebService.isOffline()).thenReturn(true); 
 ▸ Alternative

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

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

    syntax doReturn(true).when(mockWebService).isOffline(); 
 ▸ BDD syntax given(mockWebService.isOffline()).willReturn(true);
  44. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  45. @MOLSJEROEN CAPTURING ARGUMENTS @Test
 public void captureArguments() throws Exception {


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


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


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


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


    User user = new User(mockWebService, USER_ID, PASSWORD);
 user.login(mockLoginInterface);
 verify(mockWebService).login(responseArgumentCaptor.capture());
 Response response = responseArgumentCaptor.getValue();
 
 response.onRequestCompleted(true);
 
 verify(mockLoginInterface).onLoginSuccess();
 } UI USER WEBSERVICE LOGININTERFACE RESPONSE
  50. @MOLSJEROEN INGREDIENTS ▸ Creating a mock ▸ Verifying interactions ▸

    Stubbing methods ▸ Capturing arguments ▸ Mockito limitations
  51. @MOLSJEROEN MOCKITO LIMITATIONS ▸ Unable to mock ▸ final classes

    ▸ final methods ▸ static methods ▸ private methods ▸ hashCode() and equals()
  52. @MOLSJEROEN ▸ Model View Presenter ▸ Mock view & model

    ▸ Fully test presenter ▸ UI layer == “pass through” ▸ Dependency injection ▸ Only “inward pointing” dependencies 1. TESTING UI MODEL VIEW VIEW IMPLEMENTATION PRESENTER
  53. @MOLSJEROEN 2. RUNNING MOCKITO ON A DEVICE ▸ MODULE_HOME/build.gradle defaultConfig

    {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.4'
 androidTestCompile 'com.android.support.test:rules:0.4'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile "com.google.dexmaker:dexmaker:1.2"
 androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
 }
  54. @MOLSJEROEN 2. RUNNING MOCKITO ON A DEVICE ▸ MODULE_HOME/build.gradle defaultConfig

    {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.4'
 androidTestCompile 'com.android.support.test:rules:0.4'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile "com.google.dexmaker:dexmaker:1.2"
 androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
 }
  55. @MOLSJEROEN 2. RUNNING MOCKITO ON A DEVICE ▸ MODULE_HOME/build.gradle defaultConfig

    {
 … 
 testInstrumentationRunner 
 "android.support.test.runner.AndroidJUnitRunner"
 } dependencies {
 … 
 androidTestCompile 'com.android.support.test:runner:0.4'
 androidTestCompile 'com.android.support.test:rules:0.4'
 androidTestCompile 'org.mockito:mockito-core:1.10.19'
 androidTestCompile "com.google.dexmaker:dexmaker:1.2"
 androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
 }
  56. @MOLSJEROEN 3. 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
 }
 }
  57. @MOLSJEROEN 3. 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
 }
 }
  58. @MOLSJEROEN 3. 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);
 }
 }
  59. @MOLSJEROEN 3. 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);
 }
 }
  60. @MOLSJEROEN 3. 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);
 }
 }
  61. @MOLSJEROEN 3. 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();
 }
 }
  62. @MOLSJEROEN 3. 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();
 }
 }
  63. @MOLSJEROEN 4. 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
 }
  64. @MOLSJEROEN 4. 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";
 } 
 ... }
  65. @MOLSJEROEN 4. 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";
 } 
 ... }
  66. @MOLSJEROEN 5. NEW CREATION public User(int userId, String password) {


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


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


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


    this.webService = new WebService();
 } ▸ Factory pattern public User(WebServiceFactory factory, int userId, String password) {
 this.webService = factory.createWebService();
 }
  70. @MOLSJEROEN 6. POWER TIPS ▸ Avoid singletons ▸ Avoid instrumentation

    tests ▸ Isolate non testable code ▸ Don’t use Spies ▸ Don’t nest mocks ▸ Avoid using “For testing methods”
  71. IT IS BETTER TO HAVE A SIMPLE TEST THAT WORKS

    THAN A COMPLICATED TEST THAT SEEMS TO WORK. Mockito documentation
  72. @MOLSJEROEN 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
  73. @MOLSJEROEN 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 ▸ Legofy https://github.com/JeroenMols/Legofy
  74. @MOLSJEROEN 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/