Android TDD
-
Upload
godfreynolan -
Category
Technology
-
view
541 -
download
3
description
Transcript of Android TDD
![Page 1: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/1.jpg)
ANDROID TDDANDROID TDD
![Page 2: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/2.jpg)
AGENDAAGENDAUnit Testing introUp and RunningUnit testing 101Tools of the tradeMockingTDDEspressoAdding TDD to existing projects
![Page 3: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/3.jpg)
UNIT TESTING INTROUNIT TESTING INTROHello WorldBenefitsAndroid Testing PyramidActivity Testing
![Page 4: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/4.jpg)
UNIT TESTING INTROUNIT TESTING INTROCatch more mistakesConfidently make more changesBuilt in regression testingExtend the life of your codebase
![Page 5: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/5.jpg)
UNIT TESTING INTROUNIT TESTING INTROpublic double add(double firstOperand, double secondOperand) { return firstOperand + secondOperand;}
@Test public void calculator_CorrectAdd_ReturnsTrue() { assertEquals(7, add(3,4); }
@Test public void calculator_CorrectAdd_ReturnsTrue() { assertEquals("Addition is broken", 7, add(3,4); }
![Page 6: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/6.jpg)
![Page 7: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/7.jpg)
![Page 8: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/8.jpg)
![Page 9: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/9.jpg)
![Page 10: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/10.jpg)
UP AND RUNNINGUP AND RUNNINGGradle versionbuild.gradle changesBuild VariantSample app
![Page 11: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/11.jpg)
![Page 12: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/12.jpg)
dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12'}
![Page 13: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/13.jpg)
![Page 14: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/14.jpg)
![Page 15: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/15.jpg)
UNIT TESTING 101UNIT TESTING 101Command lineSetup and TeardownGroupingParametersAssertionsCode Coverage
![Page 16: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/16.jpg)
C:\Users\godfrey\AndroidStudioProjects\BasicSample>gradlew test --continueDownloading https://services.gradle.org/distributions/gradle-2.2.1-all.zip..................................................................................................................................Unzipping C:\Users\godfrey\.gradle\wrapper\dists\gradle-2.2.1-all\6dibv5rcnnqlfbq9klf8imrndn\gradleDownload https://jcenter.bintray.com/com/google/guava/guava/17.0/guava-17.0.jarDownload https://jcenter.bintray.com/com/android/tools/lint/lint-api/24.2.3/lint-api-24.2.3.jarDownload https://jcenter.bintray.com/org/ow2/asm/asm-analysis/5.0.3/asm-analysis-5.0.3.jarDownload https://jcenter.bintray.com/com/android/tools/external/lombok/lombok-ast/0.2.3/lombok-ast-0.2.3.jar:app:preBuild UP-TO-DATE:app:preDebugBuild UP-TO-DATE:app:checkDebugManifest:app:prepareDebugDependencies:app:compileDebugAidl:app:compileDebugRenderscript...:app:compileReleaseUnitTestSources:app:assembleReleaseUnitTest:app:testRelease:app:test
BUILD SUCCESSFUL
Total time: 3 mins 57.013 secs
![Page 17: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/17.jpg)
public class CalculatorTest {
private Calculator mCalculator;
@Before public void setUp() { mCalculator = new Calculator(); }
@Test public void calculator_CorrectAdd_ReturnsTrue() { double resultAdd = mCalculator.add(3, 4); assertEquals(7, resultAdd,0); }
@After public void tearDown() { mCalculator = null; }}
![Page 18: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/18.jpg)
import android.test.suitebuilder.annotation.SmallTest;
@SmallTestpublic void calculator_CorrectSub_Small_ReturnsTrue() { assertEquals(mCalculator.sub(4, 3),1,0);}
android { //.... defaultConfig { //.... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArgument "size", "small" }}
![Page 19: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/19.jpg)
@RunWith(Parameterized.class)public class CalculatorParamTest {
private int mOperandOne, mOperandTwo, mExpectedResult; private Calculator mCalculator;
@Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][] { {3, 4, 7}, {4, 3, 7}, {8, 2, 10}, {-1, 4, 3}, {3256, 4, 3260} }); }
public CalculatorParamTest(int mOperandOne, int mOperandTwo, int mExpectedResult) { this.mOperandOne = mOperandOne; this.mOperandTwo = mOperandTwo; this.mExpectedResult = mExpectedResult; }
@Before public void setUp() { mCalculator = new Calculator(); }
@Test public void testAdd_TwoNumbers() { int resultAdd = mCalculator.add(mOperandOne, mOperandTwo); assertEquals(mExpectedResult, resultAdd, 0); }}
![Page 20: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/20.jpg)
UNIT TESTING 101UNIT TESTING 101assertEqualsassertTrueassertFalseassertNullassertNotNullassertSameassertNotSameassertThatfail
![Page 21: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/21.jpg)
![Page 22: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/22.jpg)
TOOLS OF THE TRADETOOLS OF THE TRADEHamcrestMockitoRobolecticJenkins
![Page 23: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/23.jpg)
dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3'}
@Testpublic void calculator_CorrectHamAdd_ReturnsTrue() { assertThat(both(greaterThan(6)).and(lessThan(8)), mCalculator.add(3, 4));}
![Page 24: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/24.jpg)
dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19'}
@SmallTestpublic class DatabaseTest { private User joeSmith = new User("Joe", "Smith"); private final int USER_ID = 1;
@Test public void testMockUser() { //mock SQLHelper SQLHelper dbHelper = Mockito.mock(SQLHelper.class); //have mockito return joeSmith when calling dbHelper getUser Mockito.when(dbHelper.getUser(USER_ID)).thenReturn(joeSmith);
//Assert joeSmith is returned by getUser Assert.assertEquals(dbHelper.getUser(USER_ID), joeSmith); }}
![Page 25: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/25.jpg)
dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' testCompile "org.robolectric:robolectric:3.0"}
@RunWith(RobolectricGradleTestRunner.class)@Config(constants = BuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")public class RobolectricUnitTest { @Test public void shouldHaveHappySmiles() throws Exception { String hello = new MainActivity().getResources().getString(R.string.hello_world); assertThat(hello, equalTo("Hello world!")); }}
![Page 26: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/26.jpg)
@RunWith(RobolectricGradleTestRunner.class)@Config(constants = BuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml")public class ZodiacUnitTest { private ListView listView; private String[] zodiacSigns;
@Before public void setUp() { MainActivity mainActivity = Robolectric.buildActivity(MainActivity.class).create().get(); assertNotNull("Main Activity not setup", mainActivity); listView=(ListView) mainActivity.findViewById(R.id.list_of_signs); zodiacSigns = RuntimeEnvironment.application.getResources().getStringArray(R.array.zodiac_array); }
@Test public void listLoaded() throws Exception { assertThat("should be a dozen star signs", zodiacSigns.length, equalTo(listView.getCount())); }}
![Page 27: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/27.jpg)
![Page 28: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/28.jpg)
![Page 29: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/29.jpg)
![Page 30: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/30.jpg)
MOCKINGMOCKINGShared PrefsDatabaseSystem PropertiesWeb Services
![Page 31: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/31.jpg)
MOCKING TEMPLATEMOCKING TEMPLATE @Test public void test() throws Exception {
// Arrange, prepare behavior Helper aMock = mock(Helper.class); when(aMock.isCalled()).thenReturn(true);
// Act testee.doSomething(aMock);
// Assert - verify interactions verify(aMock).isCalled(); }
when(methodIsCalled).thenReturn(aValue);
![Page 32: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/32.jpg)
@RunWith(MockitoJUnitRunner.class)public class UserPreferencesTest {
// Use Mockito to initialize UserPreferences public UserPreferences tUserPreferences = Mockito.mock(UserPreferences.class); private Activity tActivity;
@Before public void setUp() { // Use Mockito to declare the return value of getSharedPreferences() Mockito.when(tUserPreferences.getSharedPreferences(tActivity)).thenReturn("true"); }
@Test public void sharedPreferencesTest_ReturnsTrue() { // Test Assert.assertThat(tUserPreferences.getSharedPreferences(tActivity), is("true")); }}
![Page 33: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/33.jpg)
@RunWith(MockitoJUnitRunner.class)public class DatabaseTest { private User joeSmith = new User("Joe", "Smith"); private final int USER_ID = 1;
@Test public void testMockUser() { //mock SQLHelper SQLHelper dbHelper = Mockito.mock(SQLHelper.class); //have mockito return joeSmith when calling dbHelper getUser Mockito.when(dbHelper.getUser(USER_ID)).thenReturn(joeSmith);
//Assert joeSmith is returned by getUser Assert.assertEquals(dbHelper.getUser(USER_ID), joeSmith); }}
![Page 34: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/34.jpg)
@RunWith(MockitoJUnitRunner.class)public class AudioHelperTest { private final int MAX_VOLUME = 100;
@Test public void maximizeVolume_Maximizes_Volume() { // Create an AudioManager object using Mockito AudioManager audioManager = Mockito.mock(AudioManager.class); // Inform Mockito what to return when audioManager.getStreamMaxVolume is called Mockito.when(audioManager.getStreamMaxVolume(AudioManager.STREAM_RING)).thenReturn(MAX_VOLUME);
new AudioHelper().maximizeVolume(audioManager); // verify with Mockito that setStreamVolume to 100 was called. Mockito.verify(audioManager).setStreamVolume(AudioManager.STREAM_RING, MAX_VOLUME, 0); }}
![Page 35: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/35.jpg)
@RunWith(MockitoJUnitRunner.class)public class TowerRetrieverTest { private static final String SUCCESS_STRING = "success";
@Test public void towerRetrievalTest() { // Use Mockito to initialize the WebService TowerRetriever towerRetriever = Mockito.mock(TowerRetriever.class);
// Use Mockito to declare the return value as SUCCESS_STRING Mockito.when(towerRetriever.send("0", "0", "0")).thenReturn(SUCCESS_STRING); // Test Assert.assertEquals(SUCCESS_STRING, towerRetriever.send("0", "0", "0")); }}
![Page 36: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/36.jpg)
TEST DRIVEN DEVELOPMENT (TDD)TEST DRIVEN DEVELOPMENT (TDD)Unit testing vs TDDWhy TDDSample appLessons learned
![Page 37: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/37.jpg)
TEST DRIVEN DEVELOPMENT TEST DRIVEN DEVELOPMENT Write test first See it fail Write simplest possible solutionto get test to pass Refactor Wash, Rinse, Repeat
![Page 38: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/38.jpg)
TEST DRIVEN DEVELOPMENT TEST DRIVEN DEVELOPMENT Built in regression testingLonger life for your codebase YAGNI feature developmentRed/Green/Refactor helpskill procrastination
![Page 39: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/39.jpg)
TDDTDDYou can't TDD w/o unit testing
TDD means writing the testsbefore the code
TDD is more painless thanclassic unit testing
UNIT TESTINGUNIT TESTINGYou can unit test w/o TDD
Unit tests don't mandate whenyou write the tests
Unit tests are often written atthe end of a coding cycle
![Page 40: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/40.jpg)
SAMPLE APP - DAILY HOROSCOPESAMPLE APP - DAILY HOROSCOPEDisplay each star signDisplay information about each star signDisplay horoscope for star sign
![Page 41: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/41.jpg)
![Page 42: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/42.jpg)
@Before public void setUp() { MainActivity mainActivity = Robolectric.buildActivity(MainActivity.class).create().get(); assertNotNull("Main Activity not setup", mainActivity); listView=(ListView) mainActivity.findViewById(R.id.list_of_signs); zodiacSigns = RuntimeEnvironment.application.getResources().getStringArray(R.array.zodiac_array); }
@Test public void listLoaded() throws Exception { assertThat("should be a dozen star signs", zodiacSigns.length, equalTo(numSigns)); }
<resources> <string name="app_name">Horoscope</string> <string-array name="zodiac_array"> <item>Aries</item> <item>Taurus</item> <item>Gemini</item> <item>Cancer</item> <item>Leo</item> <item>Virgo</item> <item>Libra</item> <item>Scorpio</item> <item>Sagittarius</item> <item>Capricorn</item> <item>Aquarius</item> <item>Pisces</item> </string-array></resources>
@Testpublic void listContentCheck() { ListAdapter listViewAdapter = listView.getAdapter(); assertEquals(zodiacSigns[0], listViewAdapter.getItem(0)); assertEquals(zodiacSigns[1], listViewAdapter.getItem(1)); assertEquals(zodiacSigns[2], listViewAdapter.getItem(2)); assertEquals(zodiacSigns[3], listViewAdapter.getItem(3)); assertEquals(zodiacSigns[4], listViewAdapter.getItem(4)); assertEquals(zodiacSigns[5], listViewAdapter.getItem(5)); assertEquals(zodiacSigns[6], listViewAdapter.getItem(6)); assertEquals(zodiacSigns[7], listViewAdapter.getItem(7)); assertEquals(zodiacSigns[8], listViewAdapter.getItem(8)); assertEquals(zodiacSigns[9], listViewAdapter.getItem(9)); assertEquals(zodiacSigns[10], listViewAdapter.getItem(10)); assertEquals(zodiacSigns[11], listViewAdapter.getItem(11));}
![Page 43: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/43.jpg)
public static final Zodiac[] signs = { new Zodiac("Aries","Enterprising, Incisive, Spontaneous, Daring, Active, Courageous and Energetic, the Aries are the proverbial infants, guileless and optimistic to the fault." new Zodiac("Taurus","Known for being reliable, practical, ambitious and sensual, the people born under the Zodiac Sign Taurus have an eye for beauty.", "Bull" new Zodiac("Gemini","Gemini-born are clever and intellectual people but they can also be tense and restless.", "Twins", "June"), new Zodiac("Cancer"," The otherwise tenacious, loyal, sympathetic and strong Crabs are vulnerable in many ways.", "Crab", "July"), new Zodiac("Leo","Warm, action-oriented and driven by the desire to be loved and admired, the Leo have an air royalty about them.", "Lion", "August"), new Zodiac("Virgo","Methodical, meticulous, analytical and mentally astute, the Virgo natives are perfectionists to the core, or at least, they like to believe that they are." new Zodiac("Libra","Librans are famous for maintaining balance and harmony.", "Scales", "October"), new Zodiac("Scorpio","The Scorpio-born are strong willed and mysterious, and they know how to effortlessly grab the limelight, as they possess what it takes to achieve their goals." new Zodiac("Sagittarius","Sagittarians are born adventurers. They tend to get bored with things easily and move on with life", "Archer", "December"), new Zodiac("Capricorn","The Capricorn-born people are the most determined of the entire Zodiac.", "Goat", "January"), new Zodiac("Aquarius","The Aquarius-born people are humanitarians to the core", "Water Bearer", "February"), new Zodiac("Pisces","Pisces or the Fish is considered as the proverbial dreamers of the Zodiac.", "Fish", "March") };
@Beforepublic void setUp() { Intent intent = new Intent(RuntimeEnvironment.application, ZodiacDetailActivity.class); intent.putExtra(ZodiacDetailActivity.EXTRA_SIGN, ARIES_SIGN_INDEX); zodiacDetailActivity = Robolectric.buildActivity(ZodiacDetailActivity.class).withIntent(intent).create().get(); assertNotNull("Zodiac Detail Activity not setup", zodiacDetailActivity);}
@Testpublic void zodiacSymbolTest() throws Exception { TextView symbolTextView = (TextView) zodiacDetailActivity.findViewById(R.id.symbol); assertEquals(Zodiac.signs[ARIES_SIGN_INDEX].getSymbol(), symbolTextView.getText().toString());}
public class ZodiacDetailActivity extends Activity { public static final String EXTRA_SIGN = "ZodiacSign";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_zodiac_detail);
int signNum = (Integer)getIntent().getExtras().get(EXTRA_SIGN); Zodiac zodiac = Zodiac.signs[signNum];
TextView name = (TextView)findViewById(R.id.name); name.setText(zodiac.getName());
TextView description = (TextView)findViewById(R.id.description); description.setText(zodiac.getDescription());
TextView symbol = (TextView)findViewById(R.id.symbol); symbol.setText(zodiac.getSymbol());
TextView month = (TextView)findViewById(R.id.month); month.setText(zodiac.getMonth()); }}
![Page 44: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/44.jpg)
public class AsyncTaskParseJson extends AsyncTask<String, String, String> { String yourJsonStringUrl = "http://a.knrz.co/horoscope-api/current/"; String horoscope = "";
public AsyncTaskParseJson(Zodiac sign) { yourJsonStringUrl += sign.getName().toLowerCase(); }
@Override protected void onPreExecute() {}
@Override protected String doInBackground(String... arg0) { try { // instantiate our json parser JsonParser jParser = new JsonParser();
// get json string from url JSONObject json = jParser.getJSONFromUrl(yourJsonStringUrl); horoscope = json.getString("prediction"); horoscope = URLDecoder.decode(horoscope); } catch (Exception e) { e.printStackTrace(); }
return null; }
@Override protected void onPostExecute(String strFromDoInBg) { TextView display = (TextView) findViewById(R.id.daily); display.setText(horoscope); } }
@Test public void zodiacDailyTest() { TextView dailyTextView = (TextView) zodiacDetailActivity.findViewById(R.id.daily); assertEquals("This week try wearing less make-up when you leave the house, " + "even if it means angering the other members of KISS.", dailyTextView.getText().toString()); }
![Page 45: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/45.jpg)
LESSONS LEARNEDLESSONS LEARNEDWhat worked
No longer need emulator
Not so much
Android Activities don't work well with TDDRobolectric is your friend
![Page 46: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/46.jpg)
ESPRESSOESPRESSOGUI TestingOnViewOnDatagradlew connectedCheck
![Page 47: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/47.jpg)
![Page 48: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/48.jpg)
![Page 49: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/49.jpg)
@RunWith(AndroidJUnit4.class)@LargeTestpublic class MainActivityTest { @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<> (MainActivity.class); @Test public void helloWorldTest() { onView(withId(R.id.hello_world)) .check(matches(withText(R.string.hello_world))); }}
@Testpublic void helloWorldButtonTest(){ onView(withId(R.id.button)) .perform(click()) .check(matches(isEnabled())); }
![Page 50: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/50.jpg)
@RunWith(AndroidJUnit4.class)@LargeTestpublic class MainActivityTest { @Rule public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class); @Test public void toDoListTest(){ onData(anything()) .inAdapterView(withId(R.id.list_of_todos)).atPosition(4) .perform(click()); onView(withId(R.id.txt_selected_item)) .check(matches(withText("go to the gym"))); } }
![Page 51: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/51.jpg)
EXISTING PROJECTSEXISTING PROJECTSWay more commonEssential StepsLessons Learned
![Page 52: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/52.jpg)
STEPSSTEPSIntroduce Continuous Integration to build codeConfigure android projects for TDDAdd minimal unit tests based on existing tests, add to CIShow team how to create unit testsAdd testing code coverage metrics to CI, expect 5-10%Add Espresso testsUnit test new features or sprouts, mock existing objectsWrap or ring fence existing code, remove unused codeRefactor wrapped code to get code coverage to 60-70%
![Page 53: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/53.jpg)
package alexandria.israelferrer.com.libraryofalexandria;
import android.app.Activity;import android.content.Context;import android.content.SharedPreferences;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import android.view.ViewStub;import android.widget.BaseAdapter;import android.widget.CheckBox;import android.widget.CompoundButton;import android.widget.ImageView;import android.widget.ListView;import android.widget.RatingBar;import android.widget.TextView;
import com.google.gson.Gson;import com.google.gson.reflect.TypeToken;import com.squareup.picasso.Picasso;
import java.io.InputStream;import java.io.InputStreamReader;import java.lang.reflect.Type;import java.util.Collections;import java.util.HashSet;import java.util.LinkedList;import java.util.List;
public class MainActivity extends Activity { private static final String PACKAGE = "com.israelferrer.alexandria"; private static final String KEY_FAVS = PACKAGE + ".FAVS"; private List<ArtWork> artWorkList; private ArtWorkAdapter adapter;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ListView listView = (ListView) findViewById(R.id.listView); InputStream stream = getResources().openRawResource(R.raw.artwork); Type listType = new TypeToken<List<ArtWork>>() { }.getType(); artWorkList = new Gson().fromJson(new InputStreamReader(stream), listType); final SharedPreferences preferences = getSharedPreferences(getPackageName() , Context.MODE_PRIVATE); for (ArtWork artWork : artWorkList) { artWork.setRating(preferences.getFloat(PACKAGE + artWork.getId(), 0F)); }
adapter = new ArtWorkAdapter(); listView.setAdapter(adapter);
}
@Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; }
public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId();
//noinspection SimplifiableIfStatement if (id == R.id.filter) { adapter.orderMode(); return true; }
return super.onOptionsItemSelected(item); }
private class ArtWorkAdapter extends BaseAdapter {
private boolean isOrder; private final List<ArtWork> orderedList;
public ArtWorkAdapter() { super(); orderedList = new LinkedList<ArtWork>(); }
@Override public int getCount() { return artWorkList.size(); }
@Override public Object getItem(int position) { return artWorkList.get(position); }
@Override public long getItemId(int position) { return Long.valueOf(artWorkList.get(position).getId()); }
public void orderMode() { isOrder = !isOrder; if (isOrder) { orderedList.clear(); orderedList.addAll(artWorkList); Collections.sort(orderedList); notifyDataSetChanged(); } else { notifyDataSetChanged(); } }
@Override public View getView(int position, View convertView, ViewGroup parent) { final ArtWork artWork; if (isOrder) { artWork = orderedList.get(position); } else { artWork = artWorkList.get(position); } View row;
switch (artWork.getType()) { case ArtWork.QUOTE: row = getLayoutInflater().inflate(R.layout.text_row, null); TextView quote = (TextView) row.findViewById(R.id.quote); TextView author = (TextView) row.findViewById(R.id.author);
quote.setText("\"" + artWork.getText() + "\""); author.setText(artWork.getAuthor()); break; case ArtWork.PAINTING: final SharedPreferences preferences = getSharedPreferences(getPackageName() , Context.MODE_PRIVATE); final HashSet<String> favs = (HashSet<String>) preferences .getStringSet(KEY_FAVS, new HashSet<String>()); row = getLayoutInflater().inflate(R.layout.painting_row, null); ImageView image = (ImageView) row.findViewById(R.id.painting); TextView painter = (TextView) row.findViewById(R.id.author); painter.setText(artWork.getTitle() + " by " + artWork.getAuthor()); Picasso.with(MainActivity.this).load(artWork.getContentUrl()).fit() .into(image); RatingBar rating = (RatingBar) row.findViewById(R.id.rate); rating.setRating(artWork.getRating()); rating.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() { @Override public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { preferences.edit().putFloat(PACKAGE + artWork.getId(), rating).apply(); artWork.setRating(rating); } }); CheckBox fav = (CheckBox) row.findViewById(R.id.fav); fav.setChecked(favs.contains(artWork.getId())); fav.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { final HashSet<String> favs = new HashSet<String>((HashSet<String>) preferences .getStringSet(KEY_FAVS, new HashSet<String>())); if (isChecked) { favs.add(artWork.getId()); } else { favs.remove(artWork.getId()); } preferences.edit().putStringSet(KEY_FAVS, favs).apply(); } }); break; case ArtWork.MOVIE: case ArtWork.OPERA: row = new ViewStub(MainActivity.this); break;
default: row = getLayoutInflater().inflate(R.layout.text_row, null); } return row; }
}}
![Page 54: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/54.jpg)
![Page 55: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/55.jpg)
apply plugin: 'com.android.application'apply plugin: 'jacoco'apply plugin: 'sonar-runner'
sonarRunner{ sonarProperties{ property "sonar.host.url", "http://localhost:9000" property "sonar.jdbc.url", "jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance" property "sonar.jdbc.driverClassName","com.mysql.jdbc.Driver" property "sonar.jdbc.username","root" property "sonar.jdbc.password","root" property "sonar.projectKey", "RIIS:CropCompare" property "sonar.projectVersion", "2.0" property "sonar.projectName","CropCompare" property "sonar.java.coveragePlugin", "jacoco" property "sonar.sources","src\\main" property "sonar.tests", "src\\test" property "sonar.jacoco.reportPath", "build\\jacoco\\jacocoTest.exec" property "sonar.java.binaries", "build" property "sonar.dynamicAnalysis", "resuseReports" }}
![Page 56: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/56.jpg)
LESSONS LEARNEDLESSONS LEARNEDLook at different architectures
MVP, MVVM w/data binding
What worked
Take baby steps, Metrics should evolve….
Not so much
Don’t be driven by metrics
Remember….
You don’t need anyone’s permission to start
![Page 57: Android TDD](https://reader034.fdocuments.net/reader034/viewer/2022052412/559446e21a28ab0f0d8b4577/html5/thumbnails/57.jpg)
RESOURCESRESOURCEShttp://www.code-labs.io/codelabs/android-testing
https://developer.android.com/training/testing/unit-testing/local-unit-tests.htmlhttp://tools.android.com/tech-docs/unit-testing-support
http://riis.com/blog
https://github.com/rallat/libraryofalexandria
https://codio.com/godfreynolan/AnDevCon-Agile-Android