unit testing and android
TRANSCRIPT
Legacy instrumentation tests• JUnit3
• Tests extend from TestCase
• AndroidTestCase
• ActivityInstrumentationTestCase2
• ServiceTestCase
• …
Testing Support Library• AndroidJUnitRunner
• JUnit4 compatible
android { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }}
androidTestCompile 'com.android.support.test:runner:0.3' androidTestCompile 'com.android.support.test:rules:0.3'
Testing Support Library
• Test filtering
• @RequiresDevice - run on physical device, no emulator
• @SdkSupress - don’t run on lower Android API level
Test SizesFeature Small Medium Large
Network access No localhost only Yes
Database No Yes Yes
File system access No Yes Yes
Use external systems No Discouraged Yes
Multiple threads No Yes Yes
Sleep statements No Yes Yes
System properties No Yes Yes
Time limit (seconds) 60 300 900+
Potential problems• Manifest merger
• problems when testing libraries
• aar dependency using manifest placeholders
• e.g. ${applicationId}, ${localApplicationId}
android { defaultConfig { manifestPlaceholders =
[localApplicationId:”com.example.mylib”] }}
Problems
• It affects the device!
• Wiping contacts will wipe contacts!
• You can’t prepare all test preconditions
• e.g. you can’t dynamically change permissions
Problems
Test failed to run to completion. Reason: 'Instrumentation run failed due to 'java.lang.IllegalStateException''. Check device logcat for details
• framework for functional UI tests
• part of Android Testing Support Library
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
@Testpublic void sayHello(){ onView(withId(R.id.editText)) .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)));}
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()));
Grade and Android Studio support
• natively supported since Gradle plugin 1.1.0
• big problems in AS in previous versions
• has issues
• switching between unit & instrumentation tests
• disabled type is not indexed
Method ... not mocked.
android { testOptions { unitTests.returnDefaultValues = true } }
• Helps rarely• returns 0, false, null, …
• mocking framework
• easy to use
• compatible with Android unit testing
testCompile ‘org.mockito:mockito-core:1.10.19'
• can be used in instrumentation tests
• needs dexmaker
androidTestCompile ‘org.mockito:mockito-core:1.10.19'androidTestCompile "com.google.dexmaker:dexmaker:1.2"androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
• @RunWith(MockitoJUnitRunner.class)
• @Mock
• Mockito.mock()
• Mockito.when().thenReturn()
• when(obj.getA()).thenReturn(aInstance)
• Mockito.spy()
• wrapping a real object
• Mockito.verify()
• verify that special condition are met
• e.g. method called, called twice, …
• at first, might be difficult to use
• the ultimate mock of Android APIs
• allows custom shadows
• @RunWith(RobolectricTestRunner.class)
• Robolectric class splits into
• Robolectric
• RuntimeEnvironment
• Shadows
• ShadowApplication
• ShadowLooper
3.0-rc3
Potential problems
• difficult to search for solutions
• long history of bigger changes
• many obsolete posts
Potential problems• difficulties running on command line and in AS
• different paths
• difficulty working with resources
• RobolectricGradleTestRunner
• doesn’t work in AS
Code Coverage• unit tests
• JaCoCo
• instrumentation tests
• EMMA
• obsolete
• Google is supposedly working on JaCoCo support
JaCoCo
• enabled by default for unit tests
• generates binary report in build/jacoco
• build/jacoco/testDebugUnitTest.exec
• gradle test
Code Coverage
• don’t use it
• generates coverage-instrumented-classes
• for instrumentation tests
• coverage for instrumentation tests is not ready
buildTypes { debug { testCoverageEnabled true }}
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
• try avoid static methods
• use dependency injection
References
• Espresso
• https://code.google.com/p/android-test-kit/
• https://code.google.com/p/android-test-kit/wiki/EspressoV2CheatSheet
References• Mockito
• http://mockito.org/
• https://github.com/mockito/mockito
• Dexmaker
• https://github.com/crittercism/dexmaker
References
• Robolectric
• http://robolectric.org/
• https://github.com/robolectric/robolectric
References• code coverage
• JaCoCo
• Eclipse Public License v1.0
• http://www.eclemma.org/jacoco/trunk/index.html
• https://github.com/jacoco/jacoco
• EMMA
• Common Public License v1.0
• http://emma.sourceforge.net/