64
copyright © 2009  cod technologies ltd  www.codtech.com droidcon 2009 testing on android diego torres milano [email protected] london, december 2009

Testing on Android

  • View
    45.773

  • Download
    1

Embed Size (px)

DESCRIPTION

Droidcon London 2009

Citation preview

Page 1: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

droidcon 2009testing on android

diego torres [email protected]

london, december 2009

Page 2: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

“Never test the depth of the water with both feet.”

­­ Anonymous

Page 3: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

agenda

● test driven development

● behavior driven development

● building blocks

● your first tdd android application

● writing tests

Page 4: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

resources

● http://android.codtech.com/droidcon2009

Page 5: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

after this section you will

● understand test driven development

● be introduced to behavior driven development

● recognize test building blocks

after this section you will...

Page 6: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

what is test driven development ?

TDD is the strategy of starting the development 

process by the test cases and then provide the software that 

satisfies these tests

Page 7: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

test driven development

uml activity diagram

Page 8: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

kinds of automated tests● unit tests

– written by programmers

– for programmers

– in a programming language

● functional or acceptance tests

– written by business people and QA

– for business people

– in a high level language

check FitNesse www.fitnesse.org

Page 9: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

behavior driven development● evolution in the thinking behind TDD● common vocabulary between business and 

technology● framework of activity based on three principles

– business and technology should refer to the same system in the same way

– any system should have an identified, verifiable value to the business

– up­front analysis, design and planning all have a diminishing return http://behaviour­driven.org

http://jbehave.org

Page 10: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

scenario● text scenario

● defining steps

Given I am not logged inWhen I log in as Liz with a password JBehaverThen I should see a message, "Welcome, Liz!"

@Given("I am not logged in") public void logOut() { currentPage.click("logout"); }

Page 11: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

testing on android● android platform integrates a 

testing framework● it's based on Junit 3● supports

– unit tests– functional tests– activity tests– mock objects– utilities to simplify test

creationPortions of this page are reproduced from work created and shared by 

Google and used according to terms described in the Creative Commons 2.5 Attribution License.

Page 12: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

is this all the documentation ?

a framework for writing android test cases and 

suites. (?)

Page 13: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ActivityInstrumentationTestCase2● functional testing of single Activity● Activity  created used system infrastructure● Activity can be manipulated● Tests can be annotated with @UiThreadTest● Intents can be injected with setActivityIntent()

● sendKeys() can be used to simulate user interaction

Page 14: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

uml class diagram

Page 15: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ApplicationTestCase● test Application in controlled environment● basic lyfecycle support

– onCreate() called after createApplication()– tearDown() calls onDestroy()

● mock Context can be injected● Application can be terminated with 

terminateApplication()

Page 16: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ActivityUnitTestCase● isolated testing of a single Activity● Activity created with minimal connection to the 

system● mocked dependencies can be injected● Activity will not participate in the normal 

interactions with the system● some methods should be avoided and will throw 

exceptions if called

Page 17: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ProviderTestCase2<T>● isolated testing of a ContentProvider● uses a MockContentResolver to

– access the provider– restricts access to filesystem (db & files)– injects IsolatedContext

● environment managed by setUp() and tearDown()

Page 18: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ServiceTestCase<T>● framework to test Services● basic support for lifecycle

– onCreate() called after startService(Intent) or bindService(Intent)

– depending on how was started tearDown() calls appropriate method

● mock objects can be injected by– setApplication()– setContext()

Page 19: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

AndroidTestCase● base class that can be extended to support 

different requirements● use this if you need

– isolate components– test custom Views– test permissions– access Resources

Page 20: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

ViewAsserts● useful assertions about Views

– Views are aligned in several ways– ViewGroups contain Views– View is on screen– View has specific screen coordinates

Page 21: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

TouchUtils● use these methods to generate touch events● clickView() to simulate touching● drag...() to touch and drag● longClick() for touching and holding● scroll...() to simulate different scrolling● tapView() to touch and release quickly

Page 22: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

android.test.mockall classes has non­functional methods● MockApplication● MockContentResolver● MockContext● MockDialogInterface● MockPackageManager● MockResources

Page 23: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

“You know you've achieved perfection in design, not when you have nothing more to add, but when you have nothing more to take away.”

­­ Antoine de Saint­Exupery

Page 24: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

after this section you will

● be able to create a project and its corresponding tests project

● apply test driven development techniques

● get acquainted with android tests

after this section you will...

Page 25: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

design

we leave space for keyboard

if there's and error should be

displayed here (O)

converts temperatures

from celsius to

fahrenheit and

vice-versawhen th

e user

enters a

temperature in

one

field the other

is

updated

automatically

Page 26: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create project

Choose the build target

Page 27: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create test project

always create a test project

other values are automatically 

selected

Page 28: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create test case

to create the test for the Activity we use ActivityInstrumentationTestCase2<T>

the Activity as the class under test

create method stubs

Page 29: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

check generated test case/** * Copyright © 2009 COD Technologies Ltd. All rights reserved. */package com.codtech.android.training.tctdd1.test;

import android.test.ActivityInstrumentationTestCase2;

import com.codtech.android.training.tctdd1.TemperatureConverterActivity;

public class TemperatureConverterActivityTests extendsActivityInstrumentationTestCase2<TemperatureConverterActivity> {

/** * @param name */public TemperatureConverterActivityTests(String name) {

super("com.codtech.android.training.tctdd1",TemperatureConverterActivity.class);

setName(name);}

// setUp() and tearDown() not showed}

adjust stub constructor to pass required arguments 

to super

Page 30: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

setUp

/* (non-Javadoc) * @see android.test.ActivityInstrumentationTestCase2#setUp() */protected void setUp() throws Exception {

super.setUp();

final TemperatureConverterActivity activity = getActivity();

celsius = (EditText)activity.findViewById(com.codtech.android.training.tctdd1.R.id.celsius);

fahrenheit = (EditText)activity.findViewById(com.codtech.android.training.tctdd1.R.id.fahrenheit);

}

these constants are not yet created, we 

need to add the Views and Ids to the 

layout

create fields when necessary

Page 31: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

layout

Ids should satisfy 

previous requirements

Page 32: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

add some UI tests@SmallTestpublic void testSimpleCreate() {

assertNotNull(getActivity());

assertNotNull(celsius);assertNotNull(fahrenheit);

ViewAsserts.assertOnScreen(fahrenheit.getRootView(), celsius);ViewAsserts.assertOnScreen(celsius.getRootView(), fahrenheit);

}

@SmallTestpublic void testAlignment() {

ViewAsserts.assertRightAligned(celsius, fahrenheit);ViewAsserts.assertLeftAligned(celsius, fahrenheit);

}

@SmallTestpublic void testFiledsStartEmpty() {

assertTrue("Celsius field starts not empty", "".equals(celsius.getText().toString()));

assertTrue("Fahrenheit field starts not empty", "".equals(fahrenheit.getText().toString()));

}

Page 33: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

now some functional tests

@UiThreadTestpublic void testFahrenheitToCelsiusConversion() {

celsius.clear(); fahrenheit.clear();

final double f = 32.5;fahrenheit.requestFocus();fahrenheit.setNumber(f);celsius.requestFocus();final double c = TemperatureConverter.fahrenheitToCelsius(f);final double cr = celsius.getNumber();final double delta = Math.abs(c - cr);final String msg = "" + f + "F should be " + c + "C but was " +

cr + " (delta " + delta + ")";assertTrue(msg, delta < 0.005);

}

we don't have a class implementing 

setNumber(), getNumber() and 

clear() so we will be creating it soon

we don't have a TemperatureConverter 

either

we run the test in the UI thread

Page 34: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

and more tests

@SmallTestpublic void testCelsiusToFahrenheitConversion() {

final double c = -105.35;TouchUtils.tapView(this, celsius);sendKeys("MINUS 1 0 5 PERIOD 3 5");final double cr = celsius.getNumber();assertEquals("Send keys should have set " + c + " but set " +

cr, c, cr);final double f = TemperatureConverter.celsiusToFahrenheit(c);final double fr = fahrenheit.getNumber();final double delta = Math.abs(f - fr);final String msg = "" + c + "C should be " + f + "F but was " +

fr + " (delta " + delta + ")";assertTrue(msg, delta < 0.005);

}

create method stub in TemperatureConverter

don't run this test in UI thread

Page 35: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create EditNumber class

extend EditText

create a new 

package for views

once we create EditNumber we should refactor our main layout 

and fields

Page 36: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create TemperatureConverter class

Page 37: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

create a TestCase

test TemperatureConverter

create stubs

Page 38: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

select methods to test

Page 39: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

complete actual tests

public void testFahrenheitToCelsius() {for (double c: conversionTableDouble.keySet()) {

final double f = conversionTableDouble.get(c); final double cr = TemperatureConverter.fahrenheitToCelsius(f); final double delta = Math.abs(c - cr); final String msg = "" + f + "F should be " + c + "C but is " +

cr + " (delta " + delta + ")"; assertTrue(msg, delta < 0.0001);

}}

public void testCelsiusToFahrenheit() {for (double c: conversionTableDouble.keySet()) {

final double f = conversionTableDouble.get(c); final double fr = TemperatureConverter.celsiusToFahrenheit(c); final double delta = Math.abs(f - fr); final String msg = "" + c + "C should be " + f + "F but is " +

fr + " (delta " + delta + ")" ; assertTrue(msg, delta < 0.0001); }}

Page 40: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

test exceptionspublic void testAbsoluteZeroCelsius() {

Exception e = null;

try {TemperatureConverter.celsiusToFahrenheit(-274);

}catch (RuntimeException re) {

}

assertNotNull("Absolute zero C not detected", e);}

public void testAbsoluteZeroFahrenheit() {Exception e = null;

try {TemperatureConverter.fahrenheitToCelsius(-460);

}catch (RuntimeException re) {

e = re;}

assertNotNull("Absolute zero F not detected", e);}

Page 41: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

conversion table

private static final HashMap<Double, Double> conversionTableDouble =new HashMap<Double, Double>();

static {// initialize (c, f) pairsconversionTableDouble.put(0.0, 32.0);conversionTableDouble.put(100.0, 212.0);conversionTableDouble.put(-1.0, 30.20);conversionTableDouble.put(-100.0, -148.0);conversionTableDouble.put(32.0, 89.60);conversionTableDouble.put(-40.0, -40.0);conversionTableDouble.put(-273.0, -459.40);

}

● simple conversion table to run several tests

Page 42: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

run tests

conversion tests fail because we haven't implemented it yet

Page 43: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

TemperatureConverterpackage com.codtech.android.training.tctdd1;

public class TemperatureConverter {

public static final double ABSOLUTE_ZERO_C = -273.0d;public static final double ABSOLUTE_ZERO_F = -459.4d;

private static final String ERROR_MESSAGE_BELOW_ZERO_FMT ="Invalid temperature: %.2f%c below absolute zero";

public static double celsiusToFahrenheit(double c) {if (c < ABSOLUTE_ZERO_C) {

throw new RuntimeException(String.format(ERROR_MESSAGE_BELOW_ZERO_FMT, c, 'C'));

}return (c * 1.8d + 32);

}

public static double fahrenheitToCelsius(double f) {if (f < ABSOLUTE_ZERO_F) {

throw new RuntimeException(String.format(ERROR_MESSAGE_BELOW_ZERO_FMT, f, 'F'));

}return ((f - 32) / 1.8d);

}}

Page 44: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

run tests

tests fail now because we are doing the conversion right but we are invoking 

empty methods in EditNumber that don't 

update the ui

conversion tests succeded

Page 45: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

implement EditNumber methodspackage com.codtech.android.training.tctdd1.view;

import android.content.Context;import android.util.AttributeSet;import android.widget.EditText;

public class EditNumber extends EditText {

public EditNumber(Context context) {super(context);

}

public EditNumber(Context context, AttributeSet attrs) {super(context, attrs);

}

public void clear() {setText("");

}

public void setNumber(double f) {setText(Double.toString(f));

}

public double getNumber() {return Double.valueOf(getText().toString());

}}

convenience methods delegating to EditText

Page 46: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

run tests

a NumberFormatException because we are not changing 

the other field when some new temperature is entered and thus the field is empty

Page 47: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

onCreate/** Called when the activity is first created. */

@SuppressWarnings("unchecked")@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); celsius = (EditNumber)findViewById(R.id.celsius); fahrenheit = (EditNumber)findViewById(R.id.fahrenheit);

try { Class[] args = new Class[] { double.class }; celsius.addTextChangedListener(

new TemperatureChangedWatcher(fahrenheit, TemperatureConverter.class.getMethod(

"celsiusToFahrenheit", args))); fahrenheit.addTextChangedListener(

new TemperatureChangedWatcher(celsius,TemperatureConverter.class.getMethod(

"fahrenheitToCelsius", args))); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }

add change listener to these fields

Page 48: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

TemperatureChangedWatcherprivate static class TemperatureChangedWatcher implements TextWatcher {

private EditNumber dest;private Method convert;

public TemperatureChangedWatcher(EditNumber dest, Method convert) {this.dest = dest; this.convert = convert;

}

@Overridepublic void afterTextChanged(Editable s) {}

@Overridepublic void beforeTextChanged(CharSequence s, int start, int count,

int after) {}

@Overridepublic void onTextChanged(CharSequence s, int start, int before,

int count) {

if (!dest.hasWindowFocus() || dest.hasFocus() || s == null ) {return;

}// continued ...

Page 49: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

TemperatureChangedWatcher

final String ss = s.toString();if ( "".equals(ss) ) {

dest.clear();return;

}

try {final double result = (Double)

convert.invoke(TemperatureConverter.class,Double.parseDouble(ss));

// display only 2 decimalsdest.setNumber(Math.rint(result*100)/100);

} catch (NumberFormatException e) {// WARNING// this is generated while a number is entered,// for example just a -' so we don't want to show the error

} catch (Exception e) {dest.setError(e.getCause().getLocalizedMessage());

}}

}

Page 50: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

tests succeed

Page 51: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

temperature converter

we leave space for keyboard

if there's and error should be

displayed here (O)

converts temperatures

from celsius to

fahrenheit and

vice-versawhen th

e user

enters a

temperature in

one

field the other

is

updated

automatically

Page 52: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

instrumentation● Dev Tools   Instrumentation   TCTDD1 Tests→ →

Page 53: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

eclipse● TCTDD1Test   Run As   Android JUnit Test→ →

Page 54: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

command line

diego@bruce:\~$ adb -e shell am instrument -w -e class com.codtech.android.training.tctdd1.test.TemperatureConverterActivityTests com.codtech.android.training.tctdd1.test/android.test.InstrumentationTestRunner

com.codtech.android.training.tctdd1.test.TemperatureConverterActivityTests:.....Test results for InstrumentationTestRunner=.....Time: 5.564

OK (5 tests)

Page 55: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

EditNumberTests

AndroidTestCase

Page 56: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

select methods to test

Page 57: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

constructor and setUp

/** * @param name */public EditNumberTests(String name) {

super();setName(name);

}

/* (non-Javadoc) * @see android.test.AndroidTestCase#setUp() */protected void setUp() throws Exception {

super.setUp();mockContext = new IsolatedContext(new MockContentResolver(),

getContext());editNumber = new EditNumber(mockContext);editNumber.setFocusable(true);

}

creating a mock context

Page 58: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

complete actual tests/** * Test method for EditNumber#EditNumber(android.content.Context) */public void testEditNumberContext() {

assertNotNull(new EditNumber(mockContext));}

/** * Test method for EditNumber#EditNumber(Context, AttributeSet) */public void testEditNumberContextAttributeSet() {

// TODO// use a real AttributeSet for this test not nullassertNotNull(new EditNumber(mockContext, null));

}

/** * Test method for EditNumber#clear() */public void testClear() {

editNumber.setText("1234");editNumber.clear();assertEquals("editNumber should have been cleared",

"", editNumber.getText().toString());}

Page 59: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

complete actual tests

public void testSetNumber() {final double d = 1.23d;editNumber.setNumber(d);final double dr = editNumber.getNumber();final String msg = "editNumber should be " + d + " but is " + dr;assertEquals(msg, d, dr);

}

public void testGetNumber() {final double d = 0.98d;final String ds = Double.toString(d);editNumber.setText(ds);final double dr = editNumber.getNumber();final String msg = "editNumber should be " + ds + " but is " + dr;assertEquals(msg, d, dr);

}

Page 60: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

input testpublic class TemperatureConverterActivityTests extends

ActivityInstrumentationTestCase2<TemperatureConverterActivity> {

// …

@SmallTestpublic void testInputFilter() {

TouchUtils.tapView(this, celsius);final Double n = -1.234d;sendKeys("MINUS 1 PERIOD 2 PERIOD 3 PERIOD 4");Object nr = null;try {

nr = celsius.getNumber();}catch (NumberFormatException e) {

nr = celsius.getText();}

final String msg = "-1.2.3.4 should be filtered to " + n +" but is " + nr;

assertEquals(msg, n, nr);}

}

Page 61: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

input filter

public class EditNumber extends EditText {

// …

/** * Initialization. * Set filter. * */private void init() {

// DigistKeyListener.getInstance(true, true) returns an// instance that accepts digits, sign and decimal pointfinal InputFilter[] filters = new InputFilter[]

{ DigitsKeyListener.getInstance(true, true) };

setFilters(filters);}

}

invoke init() from constructors

Page 62: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

resources

● http://dtmilano.blogspot.com

● http://easymock.org

● http://code.google.com/p/hamcrest (matchers)

● http://mockito.org/

● http://developer.android.com/guide/developing/tools/monkey.html (ui exerciser)

● http://ricston.com/ricston­training­for­android/

● http://android.codtech.com

Page 63: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

“If things seem under control, you're not going fast enough.”

­­ Mario Andretti

Page 64: Testing on Android

copyright © 2009  cod technologies ltd  www.codtech.com

thank youtesting on android

diego torres [email protected]

london, december 2009