introduction to android testing - oscon 2012

Post on 09-May-2015

2.538 Views

Category:

Technology

10 Downloads

Preview:

Click to see full reader

DESCRIPTION

It doesn’t matter how much time you invest in Android design, or even how careful you are when programming, mistakes are inevitable and bugs will appear.This session will help you minimize the impact of these errors in your Android project and increase your development productivity. We will introduce the most commonly-available techniques, frameworks, and tools to improve the development of your Android applications.Clear, step-by-step instructions show how to write tests for your applications and assure quality control using various methodologies.

TRANSCRIPT

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

introduction to android testing

a hands-on approachOSCON 2012

1

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

diego torres milanoandroid system engineer

flextronics

http://dtmilano.blogspot.com

2

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

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

-- Anonymous

3

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

agenda

android testing background

test driven development

code coverage

continuous integration

behavior driven development

4

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

operating systems

Others

Android400M

iOS250M

May 2012

5

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

android testing background

6

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

?WhenWhat

howWhy

7

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testtests

unit functional

integration performance

8

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of test

unitby programmers for programmers

in a programming language

JUnit is the de-facto standard

test objects in isolation

in a repeatable way

usually rely on mock objects

9

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testtests

unit functional

integration performance

10

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of test

by business & QA people

in a business domain language

test completeness & correctness

BDD has gained some popularity

FitNesse can help

functional

11

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testtests

unit functional

integration performance

12

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of test

how components work together

modules have been unit tested

android components need integration with the system

testing framework facilitates integration

integration

13

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testtests

unit functional

integration performance

14

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testmeasure performance in a repeatable way

if cannot be measure cannot be improved

premature optimization does more harm than good

measure-change-measure

performance

15

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

types of testtests

unit functional

integration performance

16

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

class diagramAssert

TestCase<<iface>>Test

AndroidTestCaseInstrumentationTestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

17

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

InstrumentationTestCaseinstrumentation instantiated before application

allows for monitoring interaction

send keys and input events

manual lifecycle

direct or indirect base class of other tests

18

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

class diagramAssert

TestCase<<iface>>Test

AndroidTestCaseInstrumentationTestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

19

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

isolated testing of single Activity

minimal connection to the system

uses mocks for dependencies

some Activity methods should not be called

ActivityUnitTestCase

20

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

class diagramAssert

TestCase<<iface>>Test

AndroidTestCaseInstrumentationTestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

21

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

functionaltesting of a single Activity

has access to Instrumentation

creates the AUT using system infrastructure

custom intent can be provided

ActivityInstrumentationTestCase2

22

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

class diagramAssert

TestCase<<iface>>Test

AndroidTestCaseInstrumentationTestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

23

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

access to Context

access to Resources

base class for Application, Provider and Service test cases

Context stored in mContext field

can start more than one Activity

AndroidTestCase

24

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

class diagramAssert

TestCase<<iface>>Test

AndroidTestCaseInstrumentationTestCase

ActivityInstrumentationTestCase2

ActivityUnitTestCase

25

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test driven development

26

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test driven development

strategy of writing tests along development

test cases written prior to the code

single test added, then the code to satisfy it

advantages:

•the tests are written one way or another

•developers take more responsibility for the quality of their work

27

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

activity diagramwrite test

run

code

refactor

[passes]

[fails]

design decisions are taken in single steps and finally the code satisfying the tests is improved by refactoring it

28

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

temperature converter

100

32

Title

fahrenheit

celsius

keyboard

autoupdate

29

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

requirements

converts between temperature units

one temperature is entered and the other is updated

error is displayed in the field

right aligned, 2 decimal digits

entry fields start empty

30

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

understanding requirements

to write a test you must understand the requirement

destination is quickly identified

if requirement change, changing the corresponding test helps verify it

31

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

github

$mkdir myworkdir

$ cd myworkdir

$ git clone git://github.com/dtmilano/I2AT-OSCON-2012.git

32

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

creating the main project

TemperatureConverter uses conventional settings

33

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

select build target

TemperatureConverter uses Android 4.0.3

34

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

creating the test project

Automatically selected values for TemperatureConverterTest

project

35

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

[13:21:28 - TemperatureConverterTest] Launching instrumentation android.test.InstrumentationTestRunner on device XXX[13:21:28 - TemperatureConverterlTest-local] Failed to launch test

36

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

creating the test case

Use: ActivityInstrumentationTestCase2

as the base classTemperatureConverterActivity as

the class under test

warning due to the parameterized base class

37

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

creating test stubs

Select: onCreate(Bundle) to create a method stub

38

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

fix constructor/** * Constructor * @param name */public TemperatureConverterActivityTests(String name) { super(TemperatureConverterActivity.class); setName(name);}

39

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

junit.framework.AssertionFailedError: Not yet implemented at com.dtmilano.i2at.tc.test. TemperatureConverterActivityTests. testOnCreateBundle( TemperatureConverterActivityTests.java:50)

40

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

fixtureprotected void setUp(String name) throws Exception { super.setUp();

mActivity = getActivity(); assertNotNull(mActivity);

mCelsius = (EditText)mActivity.findViewById( com.dtmilano.i2at.tc.R.id.celsius); assertNotNull(mCelsius); mFahrenheit = (EditText)mActivity.findViewById(com...); assertNotNull(mFahrenheit);}

references the main package

41

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

layoutsatisfy the test needs

assign celsius and fahrenheit ids

42

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

fix the test! /**! * Test method for {@link TemperatureConverterActivity #onCreate(android.os.Bundle)}.! */

! public void testOnCreateBundle() {! ! assertNotNull(mActivity);! }

43

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ui tests: visibility

@SmallTestpublic void testFieldsOnScreen() { final View origin = mActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(origin, mCelsius); ViewAsserts.assertOnScreen(origin, mFahrenheit);}

44

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

annotations@SmallTest

@MediumTest

@LargeTest

@UiThreadTest

@Suppress

@FlakyTest

45

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ui tests: alignment

@SmallTestpublic void testAlignment() {! ! ViewAsserts.assertRightAligned(mCelsius, mFahrenheit);! ! ViewAsserts.assertLeftAligned(mCelsius, mFahrenheit);}

46

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ui tests: initialization

@SmallTestpublic void testFieldsShouldStartEmpty() {! ! assertTrue("".equals(mCelsius.getText() .toString()));! ! assertTrue("".equals(mFahrenheit.getText() .toString()));}

47

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ui tests: justification @SmallTestpublic void testJustification() {! ! final int expected = Gravity.RIGHT|Gravity.CENTER_VERTICAL;! ! assertEquals(expected, mCelsius.getGravity());! ! assertEquals(expected, mFahrenheit.getGravity());}

48

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the testsvideo plays on click >>>

49

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

gravityadd right and center_vertical

gravity for celsius

and fahrenheit

50

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the testsvideo plays on click >>>

51

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

temperature conversion! @UiThreadTest! public void testFahrenheitToCelsiusConversion() {! ! mCelsius.clear();! ! mFahrenheit.clear();! ! final double f = 32.5;! ! mFahrenheit.requestFocus();! ! mFahrenheit.setNumber(f);! ! mCelsius.requestFocus();! ! final double expected = TemperatureConverter.fahrenheitToCelsius(f);! ! final double actual = mCelsius.getNumber();! ! final double delta = Math.abs(expected - actual);! ! assertTrue(delta < 0.005);! }

run on the main thread

specializedclass

converterhelper

errors are underlined in red

52

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

EditNumber classEditNumber class extends EditText

53

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

TemperatureConverter

TemperatureConverter is a helper class

54

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

refactorpublic class TemperatureConverterActivityTests extends ActivityInstrumentationTestCase2< TemperatureConverterActivity> {

private TemperatureConverterActivity mActivity; private EditNumber mCelsius; private EditNumber mFahrenheit;

new class

55

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

! @UiThreadTest! public void testFahrenheitToCelsiusConversion() {! ! mCelsius.clear();! ! mFahrenheit.clear();! ! final double f = 32.5;! ! mFahrenheit.requestFocus();! ! mFahrenheit.setNumber(f);! ! mCelsius.requestFocus();! ! final double expected = TemperatureConverter.fahrenheitToCelsius(f);! ! final double actual = mCelsius.getNumber();! ! final double delta = Math.abs(expected - actual);! ! assertTrue(delta < 0.005);! }

temperature conversion! @UiThreadTest! public void testFahrenheitToCelsiusConversion() {! ! mCelsius.clear();! ! mFahrenheit.clear();! ! final double f = 32.5;! ! mFahrenheit.requestFocus();! ! mFahrenheit.setNumber(f);! ! mCelsius.requestFocus();! ! final double expected = TemperatureConverter.fahrenheitToCelsius(f);! ! final double actual = mCelsius.getNumber();! ! final double delta = Math.abs(expected - actual);! ! assertTrue(delta < 0.005);! }

all compiler errors corrected

56

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

OMG!

[TemperatureConverterTests-OSCON-2012] Test run failed: Instrumentation run failed due to 'Process crashed.'[TemperatureConverterTests-OSCON-2012] Test run finished

57

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

exception in logcatjava.lang.ClassCastException: android.widget.EditText at com.dtmilano.i2at.tc.test. TemperatureConverterActivityTests.setUp (TemperatureConverterActivityTests.java: 52)

52:!! mCelsius = (EditNumber) mActivity.findViewById( com.dtmilano.i2at.tc.R.id.celsius);

58

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

change widget typeSelect

com.dtmilano.i2at.tc.EditNumber

59

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

layout

<com.dtmilano.i2at.tc.EditNumber android:layout_height="wrap_content"! ! android:layout_width="match_parent" android:inputType="numberDecimal"! ! android:id="@+id/celsius" android:gravity="right|center_vertical">! ! <requestFocus /></com.dtmilano.i2at.tc.EditNumber>

60

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

what?

61

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

celsius to fahrenheit

-75

-37.5

0

37.5

75

112.5

150

-40 -30 -20 -10 0 10 20 30 40

Fahrenheit

Celsius

f = 9/5 * c + 32c = (f-32) * 5/9

62

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

converter

public class TemperatureConverter {

! public static double fahrenheitToCelsius(double f) { // TODO Auto-generated method stub! ! return 0;! }

}

63

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

TemperatureConverterTestsTest case as base classcreate method stubs

TemperatureConverter as CUT

64

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

method stubsselect the methods you want

stubs created for

65

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

conversion test

public final void testFahrenheitToCelsius() {! ! for (double c: sConversionTableDouble.keySet()) {! ! ! final double f = sConversionTableDouble.get(c);! ! ! final double ca = TemperatureConverter.fahrenheitToCelsius(f);! ! ! final double delta = Math.abs(ca - c);! ! ! assertTrue(delta < 0.005);! ! }! }

66

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

conversion tableprivate static final HashMap<Double, Double> sConversionTableDouble =! ! ! new HashMap<Double, Double>();!static {! ! sConversionTableDouble.put(0.0, 32.0);! ! sConversionTableDouble.put(100.0, 212.0);! ! sConversionTableDouble.put(-1.0, 30.20);! ! sConversionTableDouble.put(-100.0, -148.0);! ! sConversionTableDouble.put(32.0, 89.60);! ! sConversionTableDouble.put(-40.0, -40.0);! ! sConversionTableDouble.put(-273.0, -459.40);}

67

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

add f -> c conversion

public class TemperatureConverter {

! public static double fahrenheitToCelsius(double f) {! ! return (f-32) * 5/9.0;! }

}

68

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

run the tests

this assertion fails

69

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

creating test case

use AndroidTestCase as base class

use EditNumber as CUT

70

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

method stubsselect the constructorsselect other methods

71

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

fixture

/* (non-Javadoc) * @see android.test.AndroidTestCase#setUp() */protected void setUp() throws Exception { super.setUp(); mEditNumber = new EditNumber(getContext());

}

72

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test

public final void testClear() {! ! final String value = "123.45";! ! mEditNumber.setText(value);! ! mEditNumber.clear();! ! final String expected = "";! ! final String actual = mEditNumber.getText().toString();! ! assertEquals(expected, actual);}

73

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

implementation

public class EditNumber extends EditText {

// ...

! public void clear() {! ! setText(null);! }

}

74

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test set number

public final void testSetNumber() { final double expected = 123.45; mEditNumber.setNumber(expected); final double actual = mEditNumber.getNumber(); assertEquals(expected, actual);}

75

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

set number

public class EditNumber extends EditText {

// ...

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

76

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test get number

public final void testGetNumber() {! ! final double expected = 123.45;! ! mEditNumber.setNumber(expected);! ! final double actual = mEditNumber.getNumber();! ! assertEquals(expected, actual);}

77

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

get numberpublic class EditNumber extends EditText {

// ...

! public void getNumber() { final String s = getText().toString(); if ( "".equals(s) ) { return Double.NaN; }! ! return Double.valueOf(s);! }}

78

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

run the tests

testFahrenheitToCelsiusConversion is still failing

79

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

what’s the problem ?clear() works

requestFocus() works

setNumber() works

fahrenheitToCelsius() works

getNumber() works

so ?

80

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

temperature converter

100

32

Title

fahrenheit

celsius

keyboard

autoupdate

81

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

TemperatureChangeWatcher

create it as an inner classcheck abstractimplements TextWatcherinherited abstract methodscreate 2 EditNumber fields mSource & mDest

82

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

generate constructor

use the fieldspublic accessomit call to super()

83

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

the watcherpublic abstract class TemperatureChangeWatcher implements TextWatcher {! ! private EditNumber mSource;! ! private EditNumber mDest;! !! ! public TemperatureChangeWatcher( EditNumber source, EditNumber dest) {! ! ! this.mSource = source;! ! ! this.mDest = dest;! ! } // ...}

84

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

on text changed@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {! if ( !mDest.hasWindowFocus() || mDest.hasFocus() || s == null ) return;! final String str = s.toString();! if ( "".equals(str) ) {! ! mDest.setText(""); return;! }! try {! ! final double result = convert(Double.parseDouble(str));! ! mDest.setNumber(result);! }! catch (NumberFormatException e) {! ! // WARNING: this is thrown while a number is entered, for example just a '-'! }! catch (Exception e) {! ! mSource.setError("ERROR: " + e.getLocalizedMessage());! }}

we should define it

85

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

abstract convert method

public abstract class TemperatureChangeWatcher implements TextWatcher { //...

protected abstract double convert(double temp); //...}

86

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

find views@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView( R.layout.activity_temperature_converter); mCelsius = (EditNumber) findViewById(R.id.celsius); mFahrenheit = (EditNumber) findViewById(R.id.fahrenheit);}

87

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

add change listeners@Overridepublic void onCreate(Bundle savedInstanceState) { // ... mCelsius.addTextChangedListener( new TemperatureChangeWatcher(mCelsius, mFahrenheit) {! ! ! @Override protected double convert(double temp) {! ! ! ! return TemperatureConverter.celsiusToFahrenheit(temp);! ! ! }! }); mFahrenheit.addTextChangedListener( new TemperatureChangeWatcher(mFahrenheit, mCelsius) {! ! ! @Override protected double convert(double temp) {! ! ! ! return TemperatureConverter.fahrenheitToCelsius(temp);! ! ! } });}

we should create it

88

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

add conversionpublic class TemperatureConverter {

! public static double fahrenheitToCelsius(double f) {! ! return (f-32) * 5/9.0;! }

! public static double celsiusToFahrenheit(double c) {! ! // TODO Auto-generated method stub! ! return 0;! }}

89

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

adding testpublic class TemperatureConverterTests extends TestCase { // ...! /**! * Test method for {@link TemperatureConverter#fahrenheitToCelsius(double)}.! */! public final void testCelsiusToFahrenheit() {! ! for (double c: sConversionTableDouble.keySet()) {! ! ! final double f = sConversionTableDouble.get(c);! ! ! final double fa = TemperatureConverter.celsiusToFahrenheit(c);! ! ! final double delta = Math.abs(fa - f);! ! ! assertTrue("delta=" + delta + " for f=" + f + " fa=" + fa, delta < 0.005);! ! }! }}

90

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

91

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

implementing conversionpublic class TemperatureConverter {

! public static double fahrenheitToCelsius(double f) {! ! return (f-32) * 5/9.0;! }

! public static double celsiusToFahrenheit(double c) {! ! return 9/5.0 * c + 32;! }}

92

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

93

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test xml attributespublic final void testSetDecimalPlacesAttributeFromXml() { LayoutInflater inflater = (LayoutInflater)getContext() .getSystemService( Context.LAYOUT_INFLATER_SERVICE); View root = inflater.inflate( com.dtmilano.i2at.tc.R.layout.activity..., null); EditNumber editNumber = (EditNumber) root.findViewById(com.dtmilano.i2at.tc.R.id.celsius); assertNotNull(editNumber); // i2at:decimalPlaces="2" is set in main.xml assertEquals(2, editNumber.getDecimalPlaces());}

94

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

decimal placespublic void setNumber(double d) { final String str = String.format("%." + mDecimalPlaces + "f", d); setText(str);}

public void setDecimalPlaces(int places) { mDecimalPlaces = places;} public int getDecimalPlaces() { return mDecimalPlaces;}

95

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

define attributes

<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="i2at"> <attr name="decimalPlaces" format="integer" /> </declare-styleable> </resources>

96

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

add attributes

xmlns:i2at="http://schemas.android.com/apk/res/com.dtmilano.i2at.tc"

<com.dtmilano.i2at.tc.EditNumber android:id="@+id/celsius" i2at:decimalPlaces="2" android:ems="10" ... > <requestFocus /> </com.dtmilano.i2at.tc.EditNumber>

97

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

init using attributes

public EditNumber(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, -1);}

private void init(Context context, AttributeSet attrs, int defStyle) { final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.i2at); setDecimalPlaces(a.getInteger( R.styleable.i2at_decimalPlaces, DEFAULT_DECIMAL_PLACES)); a.recycle();}

98

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

know decimal places

protected void setUp() throws Exception { super.setUp(); mEditNumber = new EditNumber(getContext()); mEditNumber.setDecimalPlaces(2);}

99

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test signed number@SmallTestpublic void testFahrenheitToCelsiusConversion_input() throws Throwable { runTestOnUiThread(new Runnable() { @Override public void run() { mCelsius.clear(); mFahrenheit.clear(); } }); final String c = "-123.4"; getInstrumentation().sendStringSync(c); assertEquals(c, mCelsius.getText().toString()); final double expected = TemperatureConverter.celsiusToFahrenheit(Double.parseDouble(c)); final double actual = mFahrenheit.getNumber(); final double delta = Math.abs(expected - actual); assertTrue(delta < 0.005);}

100

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

add attributes<com.dtmilano.i2at.tc.EditNumber android:id="@+id/celsius" android:layout_width="match_parent" android:layout_height="wrap_content" i2at:decimalPlaces="2" android:ems="10" android:gravity="center_vertical|right" android:inputType="numberSigned|numberDecimal" > <requestFocus /></com.dtmilano.i2at.tc.EditNumber>

101

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running the tests

102

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

code coverage

103

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

code coverage

measures the amount of source code tested

android relies on emma (http://emma.sf.net)

supported coverage types:

class

method

line

block

104

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

coverage report

overall coverage summary

overall stats summary

coverage breakdown by package

105

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

building with antdisable project’s Build Automatically in Eclipse

convert project to ant

$ android update project --path $PWD \ --name TemperatureConverter-OSCON-2012 \ --target android-15 --subprojects

$ android update test-project \ --main $PWD \ --path $PWD/tests

106

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

run configuration

run build.xml as Ant build...use emma transient target

clean, emma, debug, install,

test

107

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ant 1.8specify ant 1.8 home

Ant home...

108

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

coverage

run build.xml

coverage analysis report is generated

is currently only supported on the emulator and rooted devices

get coverage file

$ adb pull /data/data/com.example.i2at.tc/files/coverage.ec coverage.ec

109

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

on some user devs

Give the app WRITE_EXTERNAL_STORAGE permission

Add emma.dump.file build property

Use an external storage location (i.e. sdcard)

110

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

coverage report

111

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

coverage breakdown

name class% method% block% line%

TemperatureConverter

TemperatureConverterActivity

EditNumber

100% 67% 82% 67%

100% 100% 91% 93%

100% 100% 98% 96%

112

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

constructor not covered

113

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

private constructorpublic class TemperatureConverter {! private TemperatureConverter() {! ! // do nothing! }! public static double fahrenheitToCelsius(double f) {! ! return (f-32) * 5/9.0;! }! public static double celsiusToFahrenheit(double c) {! ! return 9/5.0 * c + 32;! }}

114

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

access private constructor

public final void testPrivateConstructor() throws Exception {! Constructor<TemperatureConverter> ctor = TemperatureConverter.class.getDeclaredConstructor();! ctor.setAccessible(true);! TemperatureConverter tc = ctor.newInstance((Object[])null);! assertNotNull(tc);}

circumventrestriction

115

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

returning NaN

116

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

missing tests

public final void testGetNumber_emptyText() { mEditNumber.setText(""); final double actual = mEditNumber.getNumber(); assertEquals(Double.NaN, actual);} public final void testGetNumber_nullText() { mEditNumber.setText(null); final double actual = mEditNumber.getNumber(); assertEquals(Double.NaN, actual);}

117

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

finished application

this is the final result

118

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

requirements

converts between temperature units

one temperature is entered and the other is updated

error is displayed in the field

right aligned, 2 decimal digits

entry fields start empty

119

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

running tests

120

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

alternatives

eclipse

command line

android application

121

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

run configurations122

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

am instrument

-wWaits until the

instrumentation terminates before terminating itself

-r Outputs results in raw format

-e <key> <value>

Provides testing options as key-value pairs

123

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

command line$ adb shell am instrument -w \ com.dtmilano.i2at.tc.test/\ android.test.InstrumentationTestRunner

$ adb shell am instrument -w \ -e func true \ com.dtmilano.i2at.tc.test/\ android.test.InstrumentationTestRunner

all tests:

functional tests:

124

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

Activitypublic class TemperatureConverterTestsActivity extends Activity {

! private static final String TAG = "TemperatureConverterTestsActivity";

! private static final int TIMEOUT = 15000;!! private TextView mResults;! private LogcatAsyncTask mLogcat;...

125

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

lifecycle methods@Overrideprotected void onCreate(Bundle savedInstanceState) {! ! super.onCreate(savedInstanceState);! ! setContentView(R.layout.main);! ! mResults = (TextView) findViewById(R.id.results);}@Overrideprotected void onDestroy() {! ! super.onDestroy();! ! if (mLogcat != null) { mLogcat.cancel(true); }}

126

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

getInstrumentationInfo

private InstrumentationInfo getInstrumentationInfo(final String packageName) {! ! final List<InstrumentationInfo> list = getPackageManager()! ! ! ! .queryInstrumentation(packageName, 0);! ! return (!list.isEmpty()) ? list.get(0) : null;}

127

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

run testspublic void runTests(View v) { final String pn = getPackageName().replaceFirst(".test$", ""); final InstrumentationInfo info = getInstrumentationInfo(pn); if (info != null) {! final ComponentName cn = new ComponentName(info.packageName,! ! ! info.name);! if (startInstrumentation(cn, null, null)) {! ! mLogcat = new LogcatAsyncTask();! ! mLogcat.execute(TIMEOUT);! } } else {! Toast.makeText(this,! ! ! "Cannot find instrumentation for " + pn, Toast.LENGTH_SHORT) .show();! }}

128

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

task to read logcatprivate class LogcatAsyncTask extends AsyncTask<Integer, String, Void> {! // TestRunner and silence others! private static final String CMD = "logcat -v time TestRunner:I *:S";! private BufferedReader mReader;! private Process mProc;

! public LogcatAsyncTask() {! ! try {! ! ! mProc = Runtime.getRuntime().exec(CMD);! ! ! mReader = new BufferedReader(new InputStreamReader(! ! ! ! ! mProc.getInputStream()));! ! } catch (Exception e) {! ! ! Log.e(TAG, "Creating proc", e);! ! }! }

129

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

progress update

@Overrideprotected void onProgressUpdate(String... values) {! ! if (!TextUtils.isEmpty(values[0])) {! ! ! mResults.append("\n" + values[0]);! ! }}

130

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

do in background@Overrideprotected Void doInBackground(Integer... params) {! ! final long timeout = System.currentTimeMillis() + params[0];! ! try {! ! ! do {! ! ! ! Thread.sleep(50); publishProgress(mReader.readLine());! ! ! } while (System.currentTimeMillis() < timeout);! ! } catch (Exception e) {! ! ! publishProgress("ERROR: " + e);! ! } finally {! ! ! publishProgress("END"); mProc.destroy();! ! }! ! return null;! }}

131

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

in action132

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

continuous integration

133

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

continuous integration

agile technique for software engineering

received broad adoption in recent years

prevents “integration hell”

integrate changes frequently

134

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

requirements

version control system

automated build

self tested

artifacts and tests results easy to find

135

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

installation

download war from http://jenkins-ci.org

$ java -jar jenkins.war

136

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

jenkins home

137

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

android plugin

138

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

new job

139

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

ant properties

140

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

build artifacts

141

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

build dependency

142

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

android emulator

143

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

invoke ant

144

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

Create test project

Set repository URL

Trigger build after main project

Build with ant

test project

145

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

xml test results

download xmlinstrumentationtestrunner.jar

replace instrumentation byandroid:name="com.neenbedankt.android.test.XMLInstrumentationTestRunner"

customize project configuration

146

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

junit test result

147

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

build step

PKG=com.dtmilano.i2at.tcOUTDIR=/mnt/sdcard/Android/data/$PKG/files/OUTFILE=test-results.xmlADB=/opt/android-sdk/platform-tools/adb

$ADB pull "$OUTDIR/$OUTFILE" "$WORKSPACE/TemperatureConverter-OSCON-2012/tests/bin/$OUTFILE"

148

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test report

149

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

coverage trend

Enable record emma coverage

Specify emma XML report files

Change health reporting

Modify build.xml to support XML reports

150

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

code coverage

151

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

line coverage

152

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

behavior driven development

153

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

behavior driven development

evolution of Test Driven Development

inclusion of business participant

common vocabulary

based on Neuro Linguistics Programming

154

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

fitnesse

155

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test suite

156

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

slim test fixturepackage com.example.i2at.tc.test.fitnesse.fixture;import com.example.i2at.tc.TemperatureConverter;

public class TemperatureConverterCelsiusToFahrenheitFixture {! private double celsius;

! public void setCelsius(double celsius) {! ! this.celsius = celsius;! }

! public String fahrenheit() throws Exception {! ! try {! ! ! return String.valueOf(TemperatureConverter.celsiusToFahrenheit(celsius));! ! } catch (RuntimeException e) {! ! ! return e.getLocalizedMessage();! ! }! }}

157

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

test run

158

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

questions ?

159

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

android application testing guide

Apply testing techniques and tools• Learn the nuances of Unit and

Functional testing• Understand different development

methodologies such as TDD and BDD• Apply Continuous Integration• Improve applications using

performance tests• Expose your application to a wide

range of conditions

160

Copyright (C) 2011-2012 Diego Torres Milano All rights reserved

thank you

161

top related