Introduction to android testing - oscon 2012

7,349 views

Published on

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.

Published in: Technology, Business

Introduction to android testing - oscon 2012

  1. introduction to android testing a hands-on approach OSCON 2012 Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 1
  2. diego torres milano android system engineer flextronicshttp://dtmilano.blogspot.com Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 2
  3. “Never test the depth ofthe water with both feet.” -- Anonymous Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 3
  4. agendaandroid testing backgroundtest driven developmentcode coveragecontinuous integrationbehavior driven development Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 4
  5. operating systems May 2012 Others iOS 250M Android 400MCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 5
  6. android testing backgroundCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 6
  7. WhatWhyWhen how ? Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 7
  8. types of test testsunit functionalperformance integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 8
  9. types of test by programmers for programmersunit in a programming language JUnit is the de-facto standard test objects in isolation in a repeatable way usually rely on mock objects Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 9
  10. types of test testsunit functionalperformance integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 10
  11. types of testby business & QA people functionalin a business domain languagetest completeness & correctnessBDD has gained some popularityFitNesse can help Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 11
  12. types of test testsunit functionalperformance integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 12
  13. types of testhow components work together integrationmodules have been unit testedandroid components need integrationwith the systemtesting framework facilitatesintegration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 13
  14. types of test testsunit functionalperformance integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 14
  15. types of test measure performance in aperformance repeatable way if cannot be measure cannot be improved premature optimization does more harm than good measure-change-measure Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 15
  16. types of test testsunit functionalperformance integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 16
  17. class diagram Assert <<iface>> TestCase Test InstrumentationTestCase AndroidTestCase ActivityInstrumentationTestCase2ActivityUnitTestCase Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 17
  18. InstrumentationTestCase instrumentation instantiated before application allows for monitoring interaction send keys and input events manual lifecycle direct or indirect base class of other tests Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 18
  19. class diagram Assert <<iface>> TestCase Test InstrumentationTestCase AndroidTestCase ActivityInstrumentationTestCase2ActivityUnitTestCase Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 19
  20. ActivityUnitTestCase isolated testing of single Activity minimal connection to the system uses mocks for dependencies some Activity methods should not be called Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 20
  21. class diagram Assert <<iface>> TestCase Test InstrumentationTestCase AndroidTestCase ActivityInstrumentationTestCase2ActivityUnitTestCase Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 21
  22. ActivityInstrumentationTestCase2functionaltesting of a single Activityhas access to Instrumentationcreates the AUT using systeminfrastructurecustom intent can be provided Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 22
  23. class diagram Assert <<iface>> TestCase Test InstrumentationTestCase AndroidTestCase ActivityInstrumentationTestCase2ActivityUnitTestCase Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 23
  24. access to Context AndroidTestCaseaccess to Resourcesbase class for Application, Providerand Service test casesContext stored in mContext fieldcan start more than one Activity Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 24
  25. class diagram Assert <<iface>> TestCase Test InstrumentationTestCase AndroidTestCase ActivityInstrumentationTestCase2ActivityUnitTestCase Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 25
  26. test driven development Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 26
  27. test driven development advantages: •the tests are written one way or another •developers take more responsibility for the quality of their work strategy of writing tests along development test cases written prior to the code single test added, then the code to satisfy it Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 27
  28. activity diagram design decisions are taken in single steps and finally the code write test satisfying the tests is improved by refactoring it run [fails] code[passes] refactor Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 28
  29. temperature converter Title celsius 100 autoupdate 32fahrenheit keyboard Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 29
  30. 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 emptyCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 30
  31. understanding requirementsto write a test you must understand therequirementdestination is quickly identifiedif requirement change, changing thecorresponding test helps verify it Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 31
  32. github$ mkdir myworkdir$ cd myworkdir$ git clone git://github.com/dtmilano/I2AT- OSCON-2012.git Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 32
  33. creating the main projectTemperatureConverter uses conventional settings Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 33
  34. select build targetTemperatureConverter uses Android 4.0.3 Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 34
  35. creating the test projectAutomatically selected valuesfor TemperatureConverterTest project Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 35
  36. running the tests[13:21:28 - TemperatureConverterTest]Launching instrumentationandroid.test.InstrumentationTestRunner ondevice XXX[13:21:28 - TemperatureConverterlTest-local]Failed to launch test Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 36
  37. warning due to the parameterized base classcreating the test case Use:ActivityInstrumentationTestCase2 as the base classTemperatureConverterActivity as the class under test Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 37
  38. creating test stubsSelect: onCreate(Bundle) to create a method stub Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 38
  39. fix constructor/** * Constructor * @param name */public TemperatureConverterActivityTests(String name) { super(TemperatureConverterActivity.class); setName(name);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 39
  40. running the testsjunit.framework.AssertionFailedError: Not yet implemented at com.dtmilano.i2at.tc.test. TemperatureConverterActivityTests. testOnCreateBundle( TemperatureConverterActivityTests.java:50) Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 40
  41. fixtureprotected void setUp(String name) throws Exception { super.setUp(); mActivity = getActivity(); references the assertNotNull(mActivity); main package mCelsius = (EditText)mActivity.findViewById( com.dtmilano.i2at.tc.R.id.celsius); assertNotNull(mCelsius); mFahrenheit = (EditText)mActivity.findViewById(com...); assertNotNull(mFahrenheit);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 41
  42. layoutsatisfy the test needs assign celsius and fahrenheit ids Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 42
  43. fix the test! /**! * Test method for {@link TemperatureConverterActivity #onCreate(android.os.Bundle)}.! */! public void testOnCreateBundle() {! ! assertNotNull(mActivity);! } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 43
  44. ui tests: visibility@SmallTestpublic void testFieldsOnScreen() { final View origin = mActivity.getWindow().getDecorView(); ViewAsserts.assertOnScreen(origin, mCelsius); ViewAsserts.assertOnScreen(origin, mFahrenheit);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 44
  45. annotations @SmallTest @MediumTest @LargeTest @UiThreadTest @Suppress @FlakyTestCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 45
  46. ui tests: alignment@SmallTestpublic void testAlignment() {! ! ViewAsserts.assertRightAligned(mCelsius, mFahrenheit);! ! ViewAsserts.assertLeftAligned(mCelsius, mFahrenheit);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 46
  47. ui tests: initialization@SmallTestpublic void testFieldsShouldStartEmpty() {! ! assertTrue("".equals(mCelsius.getText() .toString()));! ! assertTrue("".equals(mFahrenheit.getText() .toString()));} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 47
  48. ui tests: justification@SmallTestpublic void testJustification() {! ! final int expected = Gravity.RIGHT|Gravity.CENTER_VERTICAL;! ! assertEquals(expected, mCelsius.getGravity());! ! assertEquals(expected, mFahrenheit.getGravity());} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 48
  49. running the tests video plays on click >>>Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 49
  50. gravity add right andcenter_vertical gravity for celsius and fahrenheit Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 50
  51. running the tests video plays on click >>>Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 51
  52. temperature conversion run on the! @UiThreadTest main thread! public void testFahrenheitToCelsiusConversion() { specialized! ! mCelsius.clear(); class! ! mFahrenheit.clear(); errors are underlined in red! ! final double f = 32.5;! ! mFahrenheit.requestFocus();! ! mFahrenheit.setNumber(f); converter! ! mCelsius.requestFocus(); helper! ! final double expected = TemperatureConverter.fahrenheitToCelsius(f);! ! final double actual = mCelsius.getNumber();! ! final double delta = Math.abs(expected - actual);! ! assertTrue(delta < 0.005);! } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 52
  53. EditNumber class EditNumber class extends EditText Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 53
  54. TemperatureCon verterTemperatureConverter is a helper class Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 54
  55. refactorpublic class TemperatureConverterActivityTests extends ActivityInstrumentationTestCase2< TemperatureConverterActivity> { private TemperatureConverterActivity mActivity; new class private EditNumber mCelsius; private EditNumber mFahrenheit; Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 55
  56. temperature conversion! @UiThreadTest! public void testFahrenheitToCelsiusConversion() {! ! mCelsius.clear();! ! mFahrenheit.clear(); all compiler errors! ! final double f = 32.5; corrected! ! 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);! } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 56
  57. OMG![TemperatureConverterTests-OSCON-2012] Testrun failed: Instrumentation run failed due toProcess crashed.[TemperatureConverterTests-OSCON-2012] Testrun finished Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 57
  58. 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); Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 58
  59. change widget type Selectcom.dtmilano.i2at.tc.EditNum ber Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 59
  60. 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> Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 60
  61. running the tests what?Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 61
  62. celsius to fahrenheit Fahrenheit 150112.5 75 f = 9/5 * c + 32 37.5 0 c = (f-32) * 5/9-37.5 -75 -40 -30 -20 -10 0 10 20 30 40 Celsius Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 62
  63. converterpublic class TemperatureConverter {! public static double fahrenheitToCelsius(double f) { / TODO Auto-generated method stub /! ! return 0;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 63
  64. TemperatureConv erterTests Test case as base class create method stubs TemperatureConverter as CUT Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 64
  65. method stubsselect the methods you want stubs created for Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 65
  66. conversion testpublic 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);! ! }! } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 66
  67. 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);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 67
  68. add f -> c conversionpublic class TemperatureConverter {! public static double fahrenheitToCelsius(double f) {! ! return (f-32) * 5/9.0;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 68
  69. run the tests this assertion failsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 69
  70. creating test caseuse AndroidTestCase as base class use EditNumber as CUT Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 70
  71. method stubsselect the constructors select other methods Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 71
  72. fixture/* (non-Javadoc) * @see android.test.AndroidTestCase#setUp() */protected void setUp() throws Exception { super.setUp(); mEditNumber = new EditNumber(getContext());} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 72
  73. testpublic 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);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 73
  74. implementationpublic class EditNumber extends EditText { / ... /! public void clear() {! ! setText(null);! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 74
  75. test set numberpublic final void testSetNumber() { final double expected = 123.45; mEditNumber.setNumber(expected); final double actual = mEditNumber.getNumber(); assertEquals(expected, actual);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 75
  76. set numberpublic class EditNumber extends EditText { / ... / public void setNumber(double f) { setText(Double.toString(f)); } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 76
  77. test get numberpublic final void testGetNumber() {! ! final double expected = 123.45;! ! mEditNumber.setNumber(expected);! ! final double actual = mEditNumber.getNumber();! ! assertEquals(expected, actual);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 77
  78. get numberpublic class EditNumber extends EditText { / ... /! public void getNumber() { final String s = getText().toString(); if ( "".equals(s) ) { return Double.NaN; }! ! return Double.valueOf(s);! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 78
  79. run the tests testFahrenheitToCelsiusConversion is still failingCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 79
  80. what’s the problem ?clear() worksrequestFocus() workssetNumber() worksfahrenheitToCelsius() worksgetNumber() worksso ? Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 80
  81. temperature converter Title celsius 100 autoupdate 32fahrenheit keyboard Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 81
  82. TemperatureChan geWatcher create it as an inner class check abstract implements TextWatcher inherited abstract methods create 2 EditNumber fields mSource & mDest Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 82
  83. generate constructoruse the fieldspublic accessomit call to super() Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 83
  84. 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;! ! } / ... /} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 84
  85. 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; we should define! } it! 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());! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 85
  86. abstract convert methodpublic abstract class TemperatureChangeWatcher implements TextWatcher { //... protected abstract double convert(double temp); //...} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 86
  87. 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);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 87
  88. add change listeners@Overridepublic void onCreate(Bundle savedInstanceState) { / ... / mCelsius.addTextChangedListener( we should create new TemperatureChangeWatcher(mCelsius, mFahrenheit) { it! ! ! @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);! ! ! } });} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 88
  89. 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;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 89
  90. 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);! ! }! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 90
  91. running the testsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 91
  92. 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;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 92
  93. running the testsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 93
  94. 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());} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 94
  95. 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;} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 95
  96. define attributes<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="i2at"> <attr name="decimalPlaces" format="integer" /> </declare-styleable></resources> Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 96
  97. 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> Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 97
  98. init using attributespublic 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();} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 98
  99. know decimal placesprotected void setUp() throws Exception { super.setUp(); mEditNumber = new EditNumber(getContext()); mEditNumber.setDecimalPlaces(2);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 99
  100. 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);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 100
  101. 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> Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 101
  102. running the testsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 102
  103. code coverageCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 103
  104. code coveragemeasures the amount of source code testedandroid relies on emma (http://emma.sf.net)supported coverage types: class method line block Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 104
  105. coverage reportoverall coverage summaryoverall stats summarycoverage breakdown by package Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 105
  106. building with antdisable project’s Build Automatically in Eclipseconvert 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 Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 106
  107. run configurationrun build.xml as Ant build... clean, emma,use emma transient target debug, install, test Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 107
  108. ant 1.8specify ant 1.8 home Ant home... Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 108
  109. coveragerun build.xmlcoverage analysis report is generatedis currently only supported on the emulatorand rooted devicesget coverage file$ adb pull /data/data/com.example.i2at.tc/files/coverage.ec coverage.ec Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 109
  110. on some user devs Give the app WRITE_EXTERNAL_S TORAGE permission Add emma.dump.file build property Use an external storage location (i.e. sdcard)Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 110
  111. coverage reportCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 111
  112. coverage breakdown method name class% block% line% % TemperatureConverter 100% 67% 82% 67%TemperatureConverterActivity 100% 100% 91% 93% EditNumber 100% 100% 98% 96% Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 112
  113. constructor not covered Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 113
  114. 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;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 114
  115. access private constructorpublic final void testPrivateConstructor() throws Exception {! Constructor<TemperatureConverter> ctor = circumvent TemperatureConverter.class.getDeclaredConstructor(); restriction! ctor.setAccessible(true);! TemperatureConverter tc = ctor.newInstance((Object[])null);! assertNotNull(tc);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 115
  116. returning NaNCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 116
  117. missing testspublic 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);} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 117
  118. finished applicationthis is the final result Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 118
  119. 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 emptyCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 119
  120. running testsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 120
  121. alternativeseclipsecommand lineandroid application Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 121
  122. run configurationsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 122
  123. am instrument Waits until the -w instrumentation terminates before terminating itself Outputs results in raw -r format-e <key> Provides testing options as <value> key-value pairs Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 123
  124. command lineall tests:$ adb shell am instrument -w com.dtmilano.i2at.tc.test/ android.test.InstrumentationTestRunnerfunctional tests:$ adb shell am instrument -w -e func true com.dtmilano.i2at.tc.test/ android.test.InstrumentationTestRunner Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 124
  125. Activitypublic class TemperatureConverterTestsActivity extends Activity {! private static final String TAG = "TemperatureConverterTestsActivity";! private static final int TIMEOUT = 15000;!! private TextView mResults;! private LogcatAsyncTask mLogcat;... Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 125
  126. 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); }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 126
  127. getInstrumentationInfoprivate InstrumentationInfogetInstrumentationInfo(final String packageName) {! ! final List<InstrumentationInfo> list = getPackageManager()! ! ! ! .queryInstrumentation(packageName, 0);! ! return (!list.isEmpty()) ? list.get(0) : null;} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 127
  128. 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();! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 128
  129. 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);! ! }! } Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 129
  130. progress update@Overrideprotected void onProgressUpdate(String... values){! ! if (!TextUtils.isEmpty(values[0])) {! ! ! mResults.append("n" + values[0]);! ! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 130
  131. @Override do in backgroundprotected 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;! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 131
  132. in actionCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 132
  133. continuous integration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 133
  134. continuous integrationagile technique for software engineeringreceived broad adoption in recent yearsprevents “integration hell”integrate changes frequently Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 134
  135. requirementsversion control systemautomated buildself testedartifacts and tests results easy to find Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 135
  136. installationdownload war from http://jenkins-ci.org$ java -jar jenkins.war Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 136
  137. jenkins homeCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 137
  138. android pluginCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 138
  139. new jobCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 139
  140. ant propertiesCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 140
  141. build artifactsCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 141
  142. build dependencyCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 142
  143. android emulatorCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 143
  144. invoke antCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 144
  145. test projectCreate test projectSet repository URLTrigger build after main projectBuild with ant Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 145
  146. xml test resultsdownload xmlinstrumentationtestrunner.jarreplace instrumentation byandroid:name="com.neenbedankt.android.test.XMLInstrumentationTestRunner"customize project configuration Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 146
  147. junit test resultCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 147
  148. build stepPKG=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" Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 148
  149. test reportCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 149
  150. coverage trendEnable record emma coverageSpecify emma XML report filesChange health reportingModify build.xml to support XML reports Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 150
  151. code coverageCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 151
  152. line coverageCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 152
  153. behavior driven developmentCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 153
  154. behavior driven developmentevolution of Test Driven Developmentinclusion of business participantcommon vocabularybased on Neuro Linguistics Programming Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 154
  155. fitnesseCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 155
  156. test suiteCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 156
  157. 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();! ! }! }} Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 157
  158. test runCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 158
  159. questions ?Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 159
  160. 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 Copyright (C) 2011-2012 Diego Torres Milano All rights reserved 160
  161. thank youCopyright (C) 2011-2012 Diego Torres Milano All rights reserved 161

×