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

Android Testing Support Library - the Nitty Gritty

Android Testing Support Library - the Nitty Gritty

A talk I gave at mdevcon 2016 in Amsterdam
It goes through bits and pieces that make up the Testing Support Library

Zan Markan

March 18, 2016
Tweet

More Decks by Zan Markan

Other Decks in Programming

Transcript

  1. Testing 101 • on JVM vs on device • unit

    / integration / functional • Robolectric, Calabash, Instrumentation, Robotium
  2. “Support” • Framework vs Support libraries • Trend to unbundle

    • support-v4, appcompat-v7, recyclerview, …
  3. Good Old Times… • jUnit 3 syntax • Remember ActivityInstrumentationTestCase2?

    public class OldSchoolTest extends ActivityInstrumentationTestCase2<ResourceListActivity> {
 public OldSchoolTest() {
 super(ResourceListActivity.class);
 } }
  4. public class OldSchoolTest extends ActivityInstrumentationTestCase2<ResourceListActivity> {
 public OldSchoolTest() {
 super(ResourceListActivity.class);


    }
 
 @Override
 protected void setUp() throws Exception {
 super.setUp();
 //Omg.
 }
 
 @Override
 protected void tearDown() throws Exception {
 super.tearDown();
 //Srsly??
 }
 
 @SmallTest
 public void testMustHaveTheTestPrefix(){
 fail("No.");
 }
 
 @SmallTest
 public void test_canHaveUnderscoreInPrefixAsWell(){
 fail("Just no.");
 }
 }
  5. Welcome to the future • jUnit4 syntax • No more

    extending • @Test, @Before, @After, @AfterClass… • ActivityTestRule, InstrumentationRegistry
  6. @RunWith(AndroidJUnit4.class)
 public class ModernTest {
 
 @Rule
 ActivityTestRule<ResourceListActivity> activityRule =

    new ActivityTestRule<>(ResourceListActivity.class);
 
 @Before
 public void setupMocks(){}
 
 @After
 public void clearMocks(){}
 
 @Test
 public void anyNameYouWish(){
 //Yay
 }
 }
  7. @RunWith(AndroidJUnit4.class)
 public class ModernTest {
 
 @Rule
 ActivityTestRule<ResourceListActivity> activityRule =

    new ActivityTestRule<>(ResourceListActivity.class);
 
 @Before
 public void setupMocks(){}
 
 @After
 public void clearMocks(){}
 
 @Test
 public void anyNameYouWish(){
 //Yay
 }
 }
  8. @RunWith(AndroidJUnit4.class)
 public class ModernTest {
 
 @Rule
 ActivityTestRule<ResourceListActivity> activityRule =

    new ActivityTestRule<>(ResourceListActivity.class);
 
 @Before
 public void setupMocks(){}
 
 @After
 public void clearMocks(){}
 
 @Test
 public void anyNameYouWish(){
 //Yay
 }
 }
  9. defaultConfig {
 applicationId "com.zmarkan.testingresources"
 minSdkVersion 23
 targetSdkVersion 23
 versionCode 1


    versionName "1.0"
 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 }
 buildTypes {
 release {
 minifyEnabled false
 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 }
 }
  10. dependencies {
 
 compile 'com.android.support:appcompat-v7:23.2.1'
 compile 'com.android.support:design:23.2.1'
 
 //Unit testing


    testCompile 'junit:junit:4.12'
 
 // JUnit4 Rules
 androidTestCompile 'com.android.support.test:rules:0.5'
 // Android JUnit Runner
 androidTestCompile 'com.android.support.test:runner:0.5'
 // Espresso core
 androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
 // Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
 androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.1'
  11. Error:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.2.1) and

    test app (23.1.1) differ. See http://g.co/ androidstudio/app-test-app-conflict for details.
  12. Gradle • Set your test runner to be AndroidJUnitRunner •

    Add dependencies • Voila! • Resolve dependencies
  13. Dependency resolution • App & Test app depend on different

    lib versions • Run ./gradlew :app:dependencies
  14. Dependency resolution • App & Test app depend on different

    lib versions • Run ./gradlew :app:dependencies • Resolve with: • exclude dependency (everywhere applicable) • use ResolutionStrategy
  15. Gradle • Set your test runner to be AndroidJUnitRunner •

    Add dependencies • Resolve dependencies • Voila!
  16. Custom Matchers • Find a view in the hierarchy •

    Good for custom views & Android components • Override: • describeTo • matchesSafely
  17. interface MyView{
 boolean hasCustomProperty();
 } static BoundedMatcher withCustomPropery() {
 return

    new BoundedMatcher<Object, MyView>(MyView.class){
 
 @Override
 public void describeTo(Description description) {
 description.appendText("Custom property is enabled");
 }
 
 @Override
 protected boolean matchesSafely(MyView item) {
 return item.hasCustomProperty();
 }
 };
 } Custom Matchers
  18. Custom IdlingResource • A better way to wait • Use

    when you have stuff going on in the background • Methods to override: • getName • isIdleNow • registerIdleTransitionCallback
  19. public class CustomIdlingResource implements IdlingResource{
 
 private ResourceCallback resourceCallback;
 private

    EventBus bus;
 private boolean loadingCompleted = false;
 
 public CustomIdlingResource(EventBus bus){
 bus.register(this);
 }
 
 public void onEvent(LoadingCompletedEvent event){
 loadingCompleted = true;
 isIdleNow();
 } Custom IdlingResource
  20. @Override
 public String getName() {
 return CustomIdlingResource.class.getName();
 }
 
 @Override


    public void registerIdleTransitionCallback(
 ResourceCallback resourceCallback) {
 this.resourceCallback = resourceCallback;
 }
 
 @Override
 public boolean isIdleNow() {
 boolean idle = loadingCompleted;
 if (idle && resourceCallback != null) {
 resourceCallback.onTransitionToIdle();
 bus.unregister(this);
 }
 return idle;
 }
 } Custom IdlingResource
  21. Page Object Pattern • Build your own DSL • Every

    screen is a Page • Page-specific Assertion/Action methods • (as seen in Calabash, etc…)
  22. public class MainViewTestUtils {
 
 public void enterUserName(String text){
 //

    espresso goes here
 } public void enterPassword(String text){
 // ...
 }
 
 public void pressContinue(){
 // ...
 }
 
 public void assertErrorShown(boolean shown){
 // ...
 }
 } Page Class
  23. @Test
 public void errorShownWhenPasswordIncorrect(){
 
 MainViewTestUtils view = new MainViewTestUtils();

    
 view.enterUsername(username);
 view.enterPassword(incorrectPassword);
 view.pressContinue();
 view.assertErrorShown(true);
 } Page Class in Test
  24. UIAutomator • Interact with any installed app • Use to

    create full end to end tests • Can coexist with Espresso in the same app • Use uiautomatorviewer to find items
  25. //Inside a test, press home and click on Google search.

    UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation();
 device.pressHome();
 device.findObject(new UiSelector().descriptionContains("Google search")).clickAndWaitForNewWindow(); Syntax
  26. Test Runner • Spins up tests and runs them •

    Customise to prepare mocks • Easier run/debug via command line
  27. Extend the Runner • Use custom Application class • Provide

    mocked dependencies • Specify this new runner in build.gradle
  28. public class CustomRunner extends AndroidJUnitRunner{
 @Override
 public Application newApplication(ClassLoader cl,

    String className, Context context) 
 throws InstantiationException, IllegalAccessException, ClassNotFoundException {
 return Instrumentation.newApplication(TestApplication.class, context);
 }
 } Extend the Runner
  29. Command Line-fu • Run module/package/class/method • Run Large/Medium/Small test only

    • Shard to run in parallel • Debug without reinstalling
  30. “Simple” API • send commands to runner via adb (adb

    shell [commands]) • am instrument -w -e class your.classname#methodName 
 test.package.name/your.test.Runner.class • d.android.com/tools/testing/testing_otheride.html
  31. Test Rules • Set starting activity / Service • Replace

    ActivityInstrumentationTestCase2 • (for most cases) • Add / Extend to create more components
  32. Rules - example • MockWebServerRule - sets up MockWebServer when

    required • Source: github.com/artem-zinnatullin/qualitymatters
  33. Above & Beyond? • Espresso Web for WebViews • Running

    on Google Device Lab • JankTestHelper
  34. W E ’ R E H I R I N

    G ! mandsdigital.com/careers @mandsdigital
  35. • Twitter, Github, androidchat.co, /r/androiddev - zmarkan • www.spacecowboyrocketcompany.com •

    zan at markan dot me • ATSL - the Nitty Gritty (the book) - in progress