Download - Introduction to android testing - oscon 2012
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