There are many tools, libraries and frameworks available for Android developers to test their applications. The jungle is huge and it's not easy to find the right ones. Some frameworks are good for unit testing, some are good for instrumentation testing, and some can be used for both. Some have great capabilities but annoying weaknesses. Some are good for testing UI, other allow you to make good mocks. We will look at many frameworks, the popular ones like Mockito, Robolectric, Espresso, and some other.
Presented at GDG DevFest Pilsen 2016.
7. Android test code
• project sources
• ${module}/src/main/java
• instrumentation tests
• ${module}/src/androidTest/java
• unit tests
• ${module}/src/test/java
• full Gradle and Android Studio support
8.
9. • the essential piece of both instrumentation and unit tests
• alone can be used only for pure Java
• doesn’t provide any mocks or Android APIs
16. Testing Support Library
• JUnit test rules
• AndroidTestRule
• ServiceTestRule
• DisableOnAndroidDebug
• LogLogcatRule
• …
androidTestCompile 'com.android.support.test:rules:0.5'
17.
18. • framework for functional UI tests
• part of Android Testing Support Library
androidTestCompile
'com.android.support.test.espresso:espresso-core:2.2.2'
20. Problems
• testing on device is not isolated
• device state affects the result
• e.g. screen on/off might affect test result
onView(withId(R.id.my_view))
.check(matches(isDisplayed()));
29. • mocking framework
• easy to use
• compatible with Android unit testing
testCompile 'org.mockito:mockito-core:2.2.11'
30. • can be used also in instrumentation tests
• needs dexmaker
androidTestCompile 'org.mockito:mockito-core:2.2.11'
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
33. @RunWith(JUnit4.class)
public class ContextManagerTest {
@Test
public void testWithContext() {
Context appContext = mock(Context.class);
Mockito.when(appContext.getPackageName())
.thenReturn(“com.example.app”);
…
}
}
34. • Mockito.spy()
• wrapping a real object
• Mockito.verify()
• verify that special condition are met
• e.g. method called, method called twice, …
35. Limitations
• final classes
• opt-in incubating support in Mockito 2
• anonymous classes
• primitive types
• static methods
36.
37. • functional testing framework
• runs on JVM
• at first, might be difficult to use
• the ultimate mock of Android APIs
• provides mocks of system managers
• allows custom shadows
38. • possible to use for UI testing
• better to use for business logic
@RunWith(RobolectricTestRunner.class)
public class MyTest {
…
}
48. • describe the desired behaviour
Feature: CoffeeMaker
Scenario: a few coffees
Given I previously had 3 coffees
When I add one coffee
Then I had 4 coffees
50. • create the
mapping
public class CoffeeMakerDefs {
CoffeeMaker mCoffeeMaker = new CoffeeMaker();
@Given("^I previously had (d+) coffees$")
public void hadCoffeesPreviously(int coffees) {
mCoffeeMaker.setCoffeeCount(coffees);
}
}
51. • create the
mapping
public class CoffeeMakerDefs {
CoffeeMaker mCoffeeMaker = new CoffeeMaker();
@Given("^I previously had (d+) coffees$")
public void hadCoffeesPreviously(int coffees) {
mCoffeeMaker.setCoffeeCount(coffees);
}
@When("^I add one coffee$")
public void addCoffee() {
mCoffeeMaker.addCoffee();
}
}
52. • create the
mapping
public class CoffeeMakerDefs {
CoffeeMaker mCoffeeMaker = new CoffeeMaker();
@Given("^I previously had (d+) coffees$")
public void hadCoffeesPreviously(int coffees) {
mCoffeeMaker.setCoffeeCount(coffees);
}
@When("^I add one coffee$")
public void addCoffee() {
mCoffeeMaker.addCoffee();
}
@Then("^I had (d+) coffees$")
public void hadCoffees(int coffees) {
Assert.assertEquals(coffees, mCoffeeMaker.getCoffeeCount());
}
}
53. • place definition and mapping at the same paths!
• ${module}/src/test/java/com/example/MyMapping.java
• ${module}/src/test/resources/com/example/
MyDefinition.feature
@RunWith(Cucumber.class)
public class RunCucumberTest {
}
58. JaCoCo
• enabled by default for unit tests
• gradle test
• generates binary report in build/jacoco
• ${module}/build/jacoco/testDebugUnitTest.exec
• it’s necessary to create a JacocoReport task to obtain a readable
report
64. Rules of thumb
• prefer pure Java
• abstract away from Android APIs
• separate business logic and UI
• don’t write business logic into activities and fragments
• MVP, MVVM is a way to go
• try avoid static and final
• use dependency injection