Android Testing with JUnit & mockito

Tips, study notes, and mini-code samples

If you are not writing tests, you are writing an instant legacy code
- Michael Feathers.
It is better to have a simple test that works, other than a complicated test that seems to work
- mockito documentation.

General Testing Tips

  • All tests must run really fast.
  • All tests must be small and focused, don’t do multiple assertions in one unit test.
  • All tests must reliable, not flaky.
  • Watch out when writing tests for edge cases.
  • Don’t include UI in tests, because it’s gonna be slow.
  • Replace WebService with dummy.
  • Mimic WebService behavior.
  • Use mocks & stubs.
  • It doesn’t matter what approach you take unless you are consistent about it.
  • Be pragmatic choose you and your team what you want to test, how you want to test it and be consistent about that.
  • Avoid singletons, so you won’t get any connections between tests.
  • Isolate non-testable code, make it a manageable risk.
  • Don’t use mockito spies, you will have side effects from one test to another.
  • Don’t nest mocks.
  • Avoid using dedicated methods, just for the sake of making things testable.
  • Don’t overuse power features.
  • Consider refactoring.
  • Prefer test on the Java JVM.

Verify Types

Behavioral Verification Verifies this method is called.

State-based Verification Verifies this method is called with those specific attributes.

What is mock

  • A generated class that doesn’t do anything, used for behavior verification.
  • But it does do one thing actually, it has counters internally, that can detect which methods are called then used for behavior verification.
  • For void methods won’t return anything, for typed methods will return the default value.

What is stub

  • A hand-written class that returns a pre-defined response given certain input, used for state verification.

JUnit

How it operates

  • It uses annotations.
  • Each test is isolated.
  • Creates an instance of the class for every test case.
  • Instance variable doesn’t share state between 2 tests.
  • What happens in one test shouldn’t be affected by what happens in another.
  • You can share objects between those classes using static, but we are not supposed to be sharing things, and it’s not recommended behavior & not expected what is gonna happen from JUnit.
  • On the other hand, there are other testing libraries that share things between tests (TestNG).
// Used for our preparation
@Before
public void setUp(){...}
// Used for the clean up
@After
public void tearDown(){...}

Testing Steps

  1. GIVEN
  • Initial conditions
  • Create instances

2. WHEN

  • Actually the actions we want to trigger
  • The thing we gonna be testing

3. THEN

  • Did it work, or it didn’t work

How does JUnit @Rule work?

Rules are used to add additional functionality which applies to all tests within a test class but in a more generic way.

For instance, ExternalResource executes code before and after a test method, without having to use @Before and @After

Using an ExternalResource rather than @Before and @After gives opportunities for better code reuse; the same rule can be used from two different test classes.


http://site.mockito.org

mockito framework

Why mockito is easy-to-use

  • Very readable syntax
  • Has no learning curve
  • With annotations support
  • Mature with large community support and very popular among Android & Java developers

mockito has a limitation, you can’t mock static methods.

How to start

app/build.gradle

dependencies {
...
testImplementation 'org.mockito:mockito-core:2.10.0'
}

Initializing mockito

/**
* As a runner,
* Which is not recommended if you are willing to use other runners
* beside mockito for testing
*/
@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {...}
/**
* As a rule
*/
public class MyClassTest {
@Rule
MockitoRule mockitoRule = MockitoJUnit.rule();
...}

Verifying interactions

Examples of both Behavioral Verification & State-based verification:

// Verifies this method called once
verify
(myMock).playMethod();
// Verifies this method called n number
verify
(myMock,times(3)).playMethod();
.............,atLeast(1).............
.............,atLeastOnce()..........
.............,atMost(1)..............
.............,only().................
.............,never()................
// Using Argument Matchers
verify(myMock).playMethod(anyString);
verify(myMock).playMethod(anyInt);
// If you used one matcher in one method argument, Rest of arguments of this methods needs to be matchers
verify(myMock).logout(anyString, eq(PASSWORD));
// Verify the call happen in specific order
InOrder inOrder = inOrder(myMock);
inOrder.verify
(myMock).login(); // First this one
inOrder.verify(myMock).logout(); // Thn this one

Stubbing methods

mockito Stubbing methods (calling methods & return our preferred values):

  • Normal Syntax
when(mockitoWebService.isOffline()).thenReturn(true);
  • Alternative Syntax
/**
* This is not type safe
* You can use it if you are working with spies or Want to override * * previous stubbed method.
* Something you will rarely use
*/
doReturn(true).when(mockitoWebService).isOffline();
  • BDD Syntax
/* 
* Nothing different in functionality from Normal Syntax, Just
* written in a style like BDD
*/
given(mockitoWebService.isOffline()).willReturn(true);

Code example

// Default is false, We ask it to return true
when
(myWebServiceMock.isOffline()).thenReturn(true);
// Do the action
user.login()
// Verify the user never calls login if the device is offline
verify(myWebServiceMock.isOffline(), never()).login();
// Multiple return types, If I called first true, second false,...
when
(myWebServiceMock.isOffline()).thenReturn(true, false, true);

Capturing Arguments

Used to test Asynchronous logic:

Don’t overuse this: many of the use-cases that this can be used for can be solved by using Argument Matchers.

Argument Captor

Native type of mockito, you just annotate a member with @captor, so this is a Response captor which can capture an argument passed to a method of type Response:

@captor
private ArgumentCaptor<Response> responseCaptor;
@Test
public void captureArguments() {
User user = new User (mockWebService, USER_ID, PASSWORD);
user.login(mockLoginInterface);
/**
* We are not verification here to check if the user called login on
* web-service. No, We are calling it to get a hold of parameters
* passed to the web-service.
* So, In this case we capture the response value passed by the user
* to the web-service
*/
verify(mockWebService).login(responseCaptor.capture());
Response response = responseCaptor.getValue();
// We pretend the network request was successful
response.OnRequestCompleted(true);
// And then check the user updates the UI accordingly
verify(mockLoginInterface).onLoginSuccess();
}

Advanced mockito

UI Testing

  • We don’t wanna test the UI, but how to make sure our app is properly tested without having to inflate the UI.
  • You dumpify the UI, make it just a pass-through layer
  • We do this using the MVP:

It works,

  • Because of dependency injection
  • It’s a clean architecture “inward pointing”, all of the dependencies point in one direction, the view implementation depends on the presenter, but the presenter doesn’t depend on the view implementation directly

Testing data

You just create your own mock implementation of the data class and override all of the methods in that class & have them return a value appropriate for your testing:

public class TestUserData extends UserData {

@Override
public String getFirstName() {
return "FirstName";
}
@Override    
public String getLastName() {
return "LastName";
}
@Override    
public int getUserId() {
return 1111007;
}
}

New Creation

Avoid creating objects internally in other objects because that makes things hard to test, use dependency injection instead:

Public User(WebService webService, int user, String password){
this.webservice = webService;
...}

If the user doesn’t need one internal object but multiple of them for method calls, In that case, we can inject a web-service factory of type “factory pattern”:

Public User(WebServiceFactory factory, int user, String password){
this.webservice = factory.createWebService();
...}
Dependency injection is a bit of loaded term in the Android community, It is not more than just injecting dependencies into constructor.