65

Guide to the jungle of testing frameworks

Embed Size (px)

Citation preview

Page 1: Guide to the jungle of testing frameworks
Page 2: Guide to the jungle of testing frameworks

Unit Testing Experience on Android

Page 3: Guide to the jungle of testing frameworks
Page 4: Guide to the jungle of testing frameworks

Android apps are difficult to test

Page 5: Guide to the jungle of testing frameworks

Types of Android tests

Page 6: Guide to the jungle of testing frameworks

Types of Android tests

Instrumentation tests

Local unit tests

Page 7: Guide to the jungle of testing frameworks

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

Page 8: Guide to the jungle of testing frameworks
Page 9: Guide to the jungle of testing frameworks

• 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

Page 10: Guide to the jungle of testing frameworks

Instrumentation Tests

Page 11: Guide to the jungle of testing frameworks

Instrumentation Tests

• running on physical device or emulator

• gradle connectedAndroidTest

• ${module}/build/reports/androidTests/connected/index.html

Page 12: Guide to the jungle of testing frameworks
Page 13: Guide to the jungle of testing frameworks

Instrumentation Tests

Legacy instrumentation tests or

Testing Support Library

Page 14: Guide to the jungle of testing frameworks

Legacy Instrumentation Tests• JUnit3 • Tests extend from TestCase

• AndroidTestCase • ActivityInstrumentationTestCase2 • ServiceTestCase • …

deprecated since API level 24

Page 15: Guide to the jungle of testing frameworks

Testing Support Library• JUnit4 compatible

• AndroidJUnitRunnerandroid { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }}

dependencies { androidTestCompile 'com.android.support.test:runner:0.5'}

Page 16: Guide to the jungle of testing frameworks

Testing Support Library• JUnit test rules

• AndroidTestRule

• ServiceTestRule

• DisableOnAndroidDebug

• LogLogcatRule

• …

androidTestCompile 'com.android.support.test:rules:0.5'

Page 17: Guide to the jungle of testing frameworks
Page 18: Guide to the jungle of testing frameworks

• framework for functional UI tests

• part of Android Testing Support Library

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'

Page 19: Guide to the jungle of testing frameworks

@Testpublic void sayHello() { onView(withId(R.id.edit_text)) .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());

onView(withText("Say hello!")) .perform(click());

String expectedText = "Hello, " + STRING_TO_BE_TYPED + "!"; onView(withId(R.id.textView)) .check(matches(withText(expectedText)));}

Page 20: Guide to the jungle of testing frameworks

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()));

Page 21: Guide to the jungle of testing frameworks

Some annoyances

android.support.test.espresso.NoActivityResumedException: No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?

Page 22: Guide to the jungle of testing frameworks

Instrumentation tests are

kindaSLOOOOOW

Page 23: Guide to the jungle of testing frameworks

Unit Tests

Page 24: Guide to the jungle of testing frameworks

Unit Tests

• run on JVM

• mockable android.jar

• gradle test

• ${module}/build/reports/tests/${variant}/index.html

Page 25: Guide to the jungle of testing frameworks

…...

Page 26: Guide to the jungle of testing frameworks
Page 27: Guide to the jungle of testing frameworks

• Helps rarely • returns 0, false, null, …

Method ... not mocked.

android { testOptions { unitTests.returnDefaultValues = true } }

Page 28: Guide to the jungle of testing frameworks
Page 29: Guide to the jungle of testing frameworks

• mocking framework • easy to use • compatible with Android unit testing

testCompile 'org.mockito:mockito-core:2.2.11'

Page 30: Guide to the jungle of testing frameworks

• 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'

Page 31: Guide to the jungle of testing frameworks

@RunWith(JUnit4.class)public class ContextManagerTest {

@Mock Context mAppContext;

@Before public void setUp() { MockitoAnnotations.initMocks(this); }

@Test public void testWithContext() { … }}

Page 32: Guide to the jungle of testing frameworks

@RunWith(MockitoJUnitRunner.class)public class ContextManagerTest {

@Mock Context mAppContext;

@Test public void testWithContext() { … }}

Page 33: Guide to the jungle of testing frameworks

@RunWith(JUnit4.class)public class ContextManagerTest {

@Test public void testWithContext() { Context appContext = mock(Context.class); Mockito.when(appContext.getPackageName()) .thenReturn(“com.example.app”); … }}

Page 34: Guide to the jungle of testing frameworks

• Mockito.spy()

• wrapping a real object

• Mockito.verify()

• verify that special condition are met

• e.g. method called, method called twice, …

Page 35: Guide to the jungle of testing frameworks

Limitations

• final classes

• opt-in incubating support in Mockito 2

• anonymous classes

• primitive types

• static methods

Page 36: Guide to the jungle of testing frameworks
Page 37: Guide to the jungle of testing frameworks

• 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

Page 38: Guide to the jungle of testing frameworks

• possible to use for UI testing

• better to use for business logic

@RunWith(RobolectricTestRunner.class)public class MyTest { …}

Page 39: Guide to the jungle of testing frameworks

• Robolectric

• RuntimeEnvironment

• Shadows

• ShadowApplication

• ShadowLooper

Page 40: Guide to the jungle of testing frameworks

Potential problems

• difficult to search for solutions

• long history of bigger API changes

• many obsolete posts

Page 41: Guide to the jungle of testing frameworks
Page 42: Guide to the jungle of testing frameworks

• Can mock static methods

• Can be used together with Mockito

Page 43: Guide to the jungle of testing frameworks

@RunWith(PowerMockRunner.class)

@PrepareForTest(Static.class);

PowerMockito.mockStatic(Static.class);

Mockito.when(Static.staticMethod())

.thenReturn(value);

PowerMockito.verifyStatic(Static.class);

Page 44: Guide to the jungle of testing frameworks
Page 45: Guide to the jungle of testing frameworks

• “matchers on steroids”

• offers more complex checks

assertThat(myClass, isInstanceOf(MainActivity.class));

assertThat(myManager.getValue(), isEqualTo(someValue));

assertThat(value, isIn(listOfValues));

assertThat(value, not(isIn(listOfValues)));

Page 46: Guide to the jungle of testing frameworks
Page 47: Guide to the jungle of testing frameworks

• cross-platform BDD framework

• human-like test definitions

testCompile ‘junit:junit:4.12'testCompile ‘info.cukes:cucumber-java:1.2.5'testCompile 'info.cukes:cucumber-junit:1.2.5'

Page 48: Guide to the jungle of testing frameworks

• 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

Page 49: Guide to the jungle of testing frameworks

• create the mapping

public class CoffeeMakerDefs { CoffeeMaker mCoffeeMaker = new CoffeeMaker();

}

Page 50: Guide to the jungle of testing frameworks

• create the mapping

public class CoffeeMakerDefs { CoffeeMaker mCoffeeMaker = new CoffeeMaker(); @Given("^I previously had (\\d+) coffees$") public void hadCoffeesPreviously(int coffees) { mCoffeeMaker.setCoffeeCount(coffees); }

}

Page 51: Guide to the jungle of testing frameworks

• 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(); }

}

Page 52: Guide to the jungle of testing frameworks

• 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()); }}

Page 53: Guide to the jungle of testing frameworks

• 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 {}

Page 54: Guide to the jungle of testing frameworks

Code Coverage

Page 55: Guide to the jungle of testing frameworks

Code Coverage

• instrumentation tests

• JaCoCo

• EMMA

• obsolete

• unit tests

• JaCoCo

Page 56: Guide to the jungle of testing frameworks

Instrumentation Tests & Code Coverage• has to be explicitly enabled

• gradle createDebugCoverageReport

• ${module}/build/reports/coverage/debug/index.html

• ${module}/build/outputs/code-coverage/connected/${deviceName}-coverage.ec

• doesn’t work on some devices!!!

buildTypes { debug { testCoverageEnabled true }}

Page 57: Guide to the jungle of testing frameworks

JaCoCo

Page 58: Guide to the jungle of testing frameworks

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

Page 59: Guide to the jungle of testing frameworks
Page 60: Guide to the jungle of testing frameworks
Page 61: Guide to the jungle of testing frameworks

Good tests

Page 62: Guide to the jungle of testing frameworks

Good tests

• run in any order

• run in isolation

• run consistently

• run fast

• are orthogonal

Page 63: Guide to the jungle of testing frameworks

How to write testable apps?

Page 64: Guide to the jungle of testing frameworks

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

Page 65: Guide to the jungle of testing frameworks

Questions?