SlideShare a Scribd company logo
ANDROID TDDANDROID TDD
AGENDAAGENDA
Unit Testing intro
Up and Running
Unit testing 101
Tools of the trade
Mocking
TDD
Espresso
Adding TDD to existing projects
UNIT TESTING INTROUNIT TESTING INTRO
Hello World
Benefits
Android Testing Pyramid
Activity Testing
UNIT TESTING INTROUNIT TESTING INTRO
Catch more mistakes
Confidently make more changes
Built in regression testing
Extend the life of your codebase
UNIT TESTING INTROUNIT TESTING INTRO
public 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);
}
UP AND RUNNINGUP AND RUNNING
Gradle version
build.gradle changes
Build Variant
Sample app
dependencies {
// Unit testing dependencies.
testCompile 'junit:junit:4.12'
}
UNIT TESTING 101UNIT TESTING 101
Command line
Setup and Teardown
Grouping
Parameters
Assertions
Code Coverage
C:UsersgodfreyAndroidStudioProjectsBasicSample>gradlew test --continue
Downloading https://services.gradle.org/distributions/gradle-2.2.1-all.zip
................................................................................
..................................................
Unzipping C:Usersgodfrey.gradlewrapperdistsgradle-2.2.1-all6dibv5rcnnqlfbq9klf8imrndngr
Download https://jcenter.bintray.com/com/google/guava/guava/17.0/guava-17.0.jar
Download https://jcenter.bintray.com/com/android/tools/lint/lint-api/24.2.3/lint-api-24.2.3.jar
Download https://jcenter.bintray.com/org/ow2/asm/asm-analysis/5.0.3/asm-analysis-5.0.3.jar
Download https://jcenter.bintray.com/com/android/tools/external/lombok/lombok-ast/0.2.3/lombok-
: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
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;
}
}
import android.test.suitebuilder.annotation.SmallTest;
@SmallTest
public void calculator_CorrectSub_Small_ReturnsTrue() {
assertEquals(mCalculator.sub(4, 3),1,0);
}
android {
//....
defaultConfig {
//....
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "size", "small"
}
}
@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);
}
}
UNIT TESTING 101UNIT TESTING 101
assertEquals
assertTrue
assertFalse
assertNull
assertNotNull
assertSame
assertNotSame
assertThat
fail
TOOLS OF THE TRADETOOLS OF THE TRADE
Hamcrest
Mockito
Robolectic
Jenkins
dependencies {
// Unit testing dependencies.
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
}
@Test
public void calculator_CorrectHamAdd_ReturnsTrue() {
assertThat(both(greaterThan(6)).and(lessThan(8)), mCalculator.add(3, 4));
}
dependencies {
// Unit testing dependencies.
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
}
@SmallTest
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);
}
}
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!"));
}
}
@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_a
}
@Test
public void listLoaded() throws Exception {
assertThat("should be a dozen star signs", zodiacSigns.length, equalTo(listView.getCount())
}
}
MOCKINGMOCKING
Shared Prefs
Database
System Properties
Web Services
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);
@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"));
}
}
@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);
}
}
@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_VOL
new AudioHelper().maximizeVolume(audioManager);
// verify with Mockito that setStreamVolume to 100 was called.
Mockito.verify(audioManager).setStreamVolume(AudioManager.STREAM_RING, MAX_VOLUME, 0);
}
}
@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"));
}
}
TEST DRIVEN DEVELOPMENT (TDD)TEST DRIVEN DEVELOPMENT (TDD)
Unit testing vs TDD
Why TDD
Sample app
Lessons learned
TEST DRIVEN DEVELOPMENTTEST DRIVEN DEVELOPMENT
Write test first
See it fail
Write simplest possible solution
to get test to pass
Refactor
Wash, Rinse, Repeat
TEST DRIVEN DEVELOPMENTTEST DRIVEN DEVELOPMENT
Built in regression testing
Longer life for your codebase
YAGNI feature development
Red/Green/Refactor helps
kill procrastination
TDDTDD
You can't TDD w/o unit testing
TDD means writing the tests
before the code
TDD is more painless than
classic unit testing
UNIT TESTINGUNIT TESTING
You can unit test w/o TDD
Unit tests don't mandate when
you write the tests
Unit tests are often written at
the end of a coding cycle
SAMPLE APP - DAILY HOROSCOPESAMPLE APP - DAILY HOROSCOPE
Display each star sign
Display information about each star sign
Display horoscope for star sign
@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>
@Test
public 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));
}
public static final Zodiac[] signs = {
new Zodiac("Aries","Enterprising, Incisive, Spontaneous, Daring, Active, Courageous and Energetic, the Aries are the proverbial infants, guileless and op
new Zodiac("Taurus","Known for being reliable, practical, ambitious and sensual, the people born under the Zodiac Sign Taurus have an eye for beauty.", "
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 b
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 ta
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")
};
@Before
public 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);
}
@Test
public 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());
}
}
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());
}
LESSONS LEARNEDLESSONS LEARNED
What worked
No longer need emulator
Not so much
Android Activities don't work well with TDD
Robolectric is your friend
ESPRESSOESPRESSO
GUI Testing
OnView
OnData
gradlew connectedCheck
@RunWith(AndroidJUnit4.class)
@LargeTest
public 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)));
}
}
@Test
public void helloWorldButtonTest(){
onView(withId(R.id.button))
.perform(click())
.check(matches(isEnabled()));
}
@RunWith(AndroidJUnit4.class)
@LargeTest
public 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")));
}
}
EXISTING PROJECTSEXISTING PROJECTS
Way more common
Essential Steps
Lessons Learned
STEPSSTEPS
Introduce Continuous Integration to build code
Configure android projects for TDD
Add minimal unit tests based on existing tests, add to CI
Show team how to create unit tests
Add testing code coverage metrics to CI, expect 5-10%
Add Espresso tests
Unit test new features or sprouts, mock existing objects
Wrap or ring fence existing code, remove unused code
Refactor wrapped code to get code coverage to 60-70%
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;
}
}
}
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&charac
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","srcmain"
property "sonar.tests", "srctest"
property "sonar.jacoco.reportPath", "buildjacocojacocoTest.exec"
property "sonar.java.binaries", "build"
property "sonar.dynamicAnalysis", "resuseReports"
}
}
LESSONS LEARNEDLESSONS LEARNED
Look 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
RESOURCESRESOURCES
http://www.code-labs.io/codelabs/android-testing
https://developer.android.com/training/testing/unit-
testing/local-unit-tests.html
http://tools.android.com/tech-docs/unit-testing-support
http://riis.com/blog
https://github.com/rallat/libraryofalexandria
https://codio.com/godfreynolan/AnDevCon-Agile-Android
CONTACT INFOCONTACT INFO
godfrey@riis.com
@godfreynolan
http://slideshare.net/godfreynolan

More Related Content

What's hot

CDI do básico ao avançado
CDI do básico ao avançadoCDI do básico ao avançado
CDI do básico ao avançado
Alberto Souza
 
Test
TestTest
Test
fshimao
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
Christoffer Noring
 
Why Kotlin - Apalon Kotlin Sprint Part 1
Why Kotlin - Apalon Kotlin Sprint Part 1Why Kotlin - Apalon Kotlin Sprint Part 1
Why Kotlin - Apalon Kotlin Sprint Part 1
Kirill Rozov
 
Тестирование на Android с Dagger 2
Тестирование на Android с Dagger 2Тестирование на Android с Dagger 2
Тестирование на Android с Dagger 2
Kirill Rozov
 
My way to clean android - Android day salamanca edition
My way to clean android - Android day salamanca editionMy way to clean android - Android day salamanca edition
My way to clean android - Android day salamanca edition
Christian Panadero
 
Angular mix chrisnoring
Angular mix chrisnoringAngular mix chrisnoring
Angular mix chrisnoring
Christoffer Noring
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
Tatsuya Maki
 
Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6
Ray Ploski
 
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
Mike Nakhimovich
 
Java8 tgtbatu javaone
Java8 tgtbatu javaoneJava8 tgtbatu javaone
Java8 tgtbatu javaone
Brian Vermeer
 
Android workshop
Android workshopAndroid workshop
Android workshop
Michael Galpin
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
David Rodenas
 
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
Danny Preussler
 
Demystifying dependency Injection: Dagger and Toothpick
Demystifying dependency Injection: Dagger and ToothpickDemystifying dependency Injection: Dagger and Toothpick
Demystifying dependency Injection: Dagger and Toothpick
Danny Preussler
 
droidparts
droidpartsdroidparts
droidparts
Droidcon Berlin
 
Dependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutesDependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutes
Antonio Goncalves
 
Contextual communications and why you should care - Droidcon DE
Contextual communications and why you should care - Droidcon DEContextual communications and why you should care - Droidcon DE
Contextual communications and why you should care - Droidcon DE
Marcos Placona
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
Anton Arhipov
 

What's hot (20)

CDI do básico ao avançado
CDI do básico ao avançadoCDI do básico ao avançado
CDI do básico ao avançado
 
Test
TestTest
Test
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Why Kotlin - Apalon Kotlin Sprint Part 1
Why Kotlin - Apalon Kotlin Sprint Part 1Why Kotlin - Apalon Kotlin Sprint Part 1
Why Kotlin - Apalon Kotlin Sprint Part 1
 
Тестирование на Android с Dagger 2
Тестирование на Android с Dagger 2Тестирование на Android с Dagger 2
Тестирование на Android с Dagger 2
 
My way to clean android - Android day salamanca edition
My way to clean android - Android day salamanca editionMy way to clean android - Android day salamanca edition
My way to clean android - Android day salamanca edition
 
Angular mix chrisnoring
Angular mix chrisnoringAngular mix chrisnoring
Angular mix chrisnoring
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
 
Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6Introduction to CDI and DI in Java EE 6
Introduction to CDI and DI in Java EE 6
 
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
 
Java8 tgtbatu javaone
Java8 tgtbatu javaoneJava8 tgtbatu javaone
Java8 tgtbatu javaone
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
 
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
 
Demystifying dependency Injection: Dagger and Toothpick
Demystifying dependency Injection: Dagger and ToothpickDemystifying dependency Injection: Dagger and Toothpick
Demystifying dependency Injection: Dagger and Toothpick
 
droidparts
droidpartsdroidparts
droidparts
 
Dependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutesDependency Injection with CDI in 15 minutes
Dependency Injection with CDI in 15 minutes
 
Contextual communications and why you should care - Droidcon DE
Contextual communications and why you should care - Droidcon DEContextual communications and why you should care - Droidcon DE
Contextual communications and why you should care - Droidcon DE
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 

Viewers also liked

ELSA France "Teaching is us!"
ELSA France "Teaching is us!" ELSA France "Teaching is us!"
ELSA France "Teaching is us!"
Adrian Scarlett
 
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuroMáster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
Celestino Güemes Seoane
 
Designing organisations for the future
Designing organisations for the futureDesigning organisations for the future
Designing organisations for the future
Ed Curley
 
Research Tools for Research Cycle: From SEARCH to DISSEMINATION
Research Tools for Research Cycle: From SEARCH to DISSEMINATIONResearch Tools for Research Cycle: From SEARCH to DISSEMINATION
Research Tools for Research Cycle: From SEARCH to DISSEMINATION
Nader Ale Ebrahim
 
España frente al acoso laboral un estudio comparativo con canadá 1
España frente al acoso laboral un estudio comparativo con canadá 1España frente al acoso laboral un estudio comparativo con canadá 1
España frente al acoso laboral un estudio comparativo con canadá 1
Plataforma en la Comunidad de Madrid contra el Mobbing
 
Fé e Confiança em Deus
Fé e Confiança em DeusFé e Confiança em Deus
Fé e Confiança em Deus
wilsondani
 
The HCL Foundation
The HCL FoundationThe HCL Foundation
The HCL Foundation
HCL Technologies
 
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
diamondheadacheclinic
 
Accelerating Insight - Smart Data Lake Customer Success Stories
Accelerating Insight - Smart Data Lake Customer Success StoriesAccelerating Insight - Smart Data Lake Customer Success Stories
Accelerating Insight - Smart Data Lake Customer Success Stories
Cambridge Semantics
 
ChatBot Trendleri : Yeni Bir Mobil Deneyim
ChatBot Trendleri : Yeni Bir Mobil DeneyimChatBot Trendleri : Yeni Bir Mobil Deneyim
ChatBot Trendleri : Yeni Bir Mobil Deneyim
Aydin Ozcekic
 
Deploying Microservices as Containers
Deploying Microservices as ContainersDeploying Microservices as Containers
Deploying Microservices as Containers
Veer Muchandi
 
Gut Biomes - The Origins of Immunity ?
Gut Biomes - The Origins of Immunity ? Gut Biomes - The Origins of Immunity ?
Gut Biomes - The Origins of Immunity ?
Isla Brown
 
Barreras para el aprendizaje y la participacion. maestria en educacion especi...
Barreras para el aprendizaje y la participacion. maestria en educacion especi...Barreras para el aprendizaje y la participacion. maestria en educacion especi...
Barreras para el aprendizaje y la participacion. maestria en educacion especi...
Virita Snicker
 
Engineering DevOps Right the First Time
Engineering DevOps Right the First TimeEngineering DevOps Right the First Time
Engineering DevOps Right the First Time
Marc Hornbeek
 
Оюуны өмчийг зүй зохистой ашиглах нь
Оюуны өмчийг зүй зохистой ашиглах ньОюуны өмчийг зүй зохистой ашиглах нь
Оюуны өмчийг зүй зохистой ашиглах нь
SK Shikhikhutag
 
Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드
NAVER D2
 
Introduction to Deep Learning (NVIDIA)
Introduction to Deep Learning (NVIDIA)Introduction to Deep Learning (NVIDIA)
Introduction to Deep Learning (NVIDIA)
Rakuten Group, Inc.
 
青空文庫テキストフォーマットについて (aozorahack)
青空文庫テキストフォーマットについて (aozorahack)青空文庫テキストフォーマットについて (aozorahack)
青空文庫テキストフォーマットについて (aozorahack)
masayoshi takahashi
 
Crude Oil Price Forecast from ScenarioKnow
Crude Oil Price Forecast from ScenarioKnowCrude Oil Price Forecast from ScenarioKnow
Crude Oil Price Forecast from ScenarioKnow
HCBIM
 
WCAG 2.1 Mobile Accessibility
WCAG 2.1 Mobile AccessibilityWCAG 2.1 Mobile Accessibility
WCAG 2.1 Mobile Accessibility
Interactive Accessibility
 

Viewers also liked (20)

ELSA France "Teaching is us!"
ELSA France "Teaching is us!" ELSA France "Teaching is us!"
ELSA France "Teaching is us!"
 
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuroMáster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
Máster Comunicación Digital 2017: Negocio electrónico y tendencias de futuro
 
Designing organisations for the future
Designing organisations for the futureDesigning organisations for the future
Designing organisations for the future
 
Research Tools for Research Cycle: From SEARCH to DISSEMINATION
Research Tools for Research Cycle: From SEARCH to DISSEMINATIONResearch Tools for Research Cycle: From SEARCH to DISSEMINATION
Research Tools for Research Cycle: From SEARCH to DISSEMINATION
 
España frente al acoso laboral un estudio comparativo con canadá 1
España frente al acoso laboral un estudio comparativo con canadá 1España frente al acoso laboral un estudio comparativo con canadá 1
España frente al acoso laboral un estudio comparativo con canadá 1
 
Fé e Confiança em Deus
Fé e Confiança em DeusFé e Confiança em Deus
Fé e Confiança em Deus
 
The HCL Foundation
The HCL FoundationThe HCL Foundation
The HCL Foundation
 
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
Common Types of Migraine Headaches and Symptoms: What Kind Does Your Child Have?
 
Accelerating Insight - Smart Data Lake Customer Success Stories
Accelerating Insight - Smart Data Lake Customer Success StoriesAccelerating Insight - Smart Data Lake Customer Success Stories
Accelerating Insight - Smart Data Lake Customer Success Stories
 
ChatBot Trendleri : Yeni Bir Mobil Deneyim
ChatBot Trendleri : Yeni Bir Mobil DeneyimChatBot Trendleri : Yeni Bir Mobil Deneyim
ChatBot Trendleri : Yeni Bir Mobil Deneyim
 
Deploying Microservices as Containers
Deploying Microservices as ContainersDeploying Microservices as Containers
Deploying Microservices as Containers
 
Gut Biomes - The Origins of Immunity ?
Gut Biomes - The Origins of Immunity ? Gut Biomes - The Origins of Immunity ?
Gut Biomes - The Origins of Immunity ?
 
Barreras para el aprendizaje y la participacion. maestria en educacion especi...
Barreras para el aprendizaje y la participacion. maestria en educacion especi...Barreras para el aprendizaje y la participacion. maestria en educacion especi...
Barreras para el aprendizaje y la participacion. maestria en educacion especi...
 
Engineering DevOps Right the First Time
Engineering DevOps Right the First TimeEngineering DevOps Right the First Time
Engineering DevOps Right the First Time
 
Оюуны өмчийг зүй зохистой ашиглах нь
Оюуны өмчийг зүй зохистой ашиглах ньОюуны өмчийг зүй зохистой ашиглах нь
Оюуны өмчийг зүй зохистой ашиглах нь
 
Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드Angularjs 도입 선택 가이드
Angularjs 도입 선택 가이드
 
Introduction to Deep Learning (NVIDIA)
Introduction to Deep Learning (NVIDIA)Introduction to Deep Learning (NVIDIA)
Introduction to Deep Learning (NVIDIA)
 
青空文庫テキストフォーマットについて (aozorahack)
青空文庫テキストフォーマットについて (aozorahack)青空文庫テキストフォーマットについて (aozorahack)
青空文庫テキストフォーマットについて (aozorahack)
 
Crude Oil Price Forecast from ScenarioKnow
Crude Oil Price Forecast from ScenarioKnowCrude Oil Price Forecast from ScenarioKnow
Crude Oil Price Forecast from ScenarioKnow
 
WCAG 2.1 Mobile Accessibility
WCAG 2.1 Mobile AccessibilityWCAG 2.1 Mobile Accessibility
WCAG 2.1 Mobile Accessibility
 

Similar to Android TDD

Unit testing with mock libs
Unit testing with mock libsUnit testing with mock libs
Unit testing with mock libs
Valentin Kolesnikov
 
Navigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupNavigating the xDD Alphabet Soup
Navigating the xDD Alphabet Soup
Dror Helper
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
Tomek Kaczanowski
 
Secret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you aboutSecret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you about
Dror Helper
 
Spring mvc my Faviourite Slide
Spring mvc my Faviourite SlideSpring mvc my Faviourite Slide
Spring mvc my Faviourite Slide
Daniel Adenew
 
Testing in android
Testing in androidTesting in android
Testing in android
jtrindade
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
Tomek Kaczanowski
 
Android testing
Android testingAndroid testing
Android testing
Sean Tsai
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
Visual Engineering
 
An introduction to Test Driven Development on MapReduce
An introduction to Test Driven Development on MapReduceAn introduction to Test Driven Development on MapReduce
An introduction to Test Driven Development on MapReduce
Ananth PackkilDurai
 
Testing basics for developers
Testing basics for developersTesting basics for developers
Testing basics for developers
Anton Udovychenko
 
Asp netmvc e03
Asp netmvc e03Asp netmvc e03
Asp netmvc e03
Yu GUAN
 
Neo4j Stored Procedure Training Part 2
Neo4j Stored Procedure Training Part 2Neo4j Stored Procedure Training Part 2
Neo4j Stored Procedure Training Part 2
Max De Marzi
 
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
solit
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
First Tuesday Bergen
 
Dependency Injection for Android
Dependency Injection for AndroidDependency Injection for Android
Dependency Injection for Android
First Tuesday Bergen
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
Unity Technologies
 
Mutation testing Bucharest Tech Week
Mutation testing Bucharest Tech WeekMutation testing Bucharest Tech Week
Mutation testing Bucharest Tech Week
Paco van Beckhoven
 
The secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you aboutThe secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you about
Dror Helper
 
Presentation Unit Testing process
Presentation Unit Testing processPresentation Unit Testing process
Presentation Unit Testing process
Bárbara Cabral da Conceição, CTFL
 

Similar to Android TDD (20)

Unit testing with mock libs
Unit testing with mock libsUnit testing with mock libs
Unit testing with mock libs
 
Navigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupNavigating the xDD Alphabet Soup
Navigating the xDD Alphabet Soup
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Secret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you aboutSecret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you about
 
Spring mvc my Faviourite Slide
Spring mvc my Faviourite SlideSpring mvc my Faviourite Slide
Spring mvc my Faviourite Slide
 
Testing in android
Testing in androidTesting in android
Testing in android
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
Android testing
Android testingAndroid testing
Android testing
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
An introduction to Test Driven Development on MapReduce
An introduction to Test Driven Development on MapReduceAn introduction to Test Driven Development on MapReduce
An introduction to Test Driven Development on MapReduce
 
Testing basics for developers
Testing basics for developersTesting basics for developers
Testing basics for developers
 
Asp netmvc e03
Asp netmvc e03Asp netmvc e03
Asp netmvc e03
 
Neo4j Stored Procedure Training Part 2
Neo4j Stored Procedure Training Part 2Neo4j Stored Procedure Training Part 2
Neo4j Stored Procedure Training Part 2
 
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
 
Dependency Injection for Android
Dependency Injection for AndroidDependency Injection for Android
Dependency Injection for Android
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
 
Mutation testing Bucharest Tech Week
Mutation testing Bucharest Tech WeekMutation testing Bucharest Tech Week
Mutation testing Bucharest Tech Week
 
The secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you aboutThe secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you about
 
Presentation Unit Testing process
Presentation Unit Testing processPresentation Unit Testing process
Presentation Unit Testing process
 

More from Godfrey Nolan

Counting Cars with Drones
Counting Cars with DronesCounting Cars with Drones
Counting Cars with Drones
Godfrey Nolan
 
Customising QGroundControl
Customising QGroundControlCustomising QGroundControl
Customising QGroundControl
Godfrey Nolan
 
DJI Payload SDK
DJI Payload SDKDJI Payload SDK
DJI Payload SDK
Godfrey Nolan
 
Parrot Tutorials in Kotlin
Parrot Tutorials in KotlinParrot Tutorials in Kotlin
Parrot Tutorials in Kotlin
Godfrey Nolan
 
DJI Mobile SDK Tutorials in kotlin
DJI Mobile SDK Tutorials in kotlinDJI Mobile SDK Tutorials in kotlin
DJI Mobile SDK Tutorials in kotlin
Godfrey Nolan
 
Drone sdk showdown
Drone sdk showdownDrone sdk showdown
Drone sdk showdown
Godfrey Nolan
 
AI/ML in drones
AI/ML in dronesAI/ML in drones
AI/ML in drones
Godfrey Nolan
 
Getting started with tensor flow datasets
Getting started with tensor flow datasets Getting started with tensor flow datasets
Getting started with tensor flow datasets
Godfrey Nolan
 
Using ML to make your UI tests more robust
Using ML to make your UI tests more robustUsing ML to make your UI tests more robust
Using ML to make your UI tests more robust
Godfrey Nolan
 
Java best practices
Java best practicesJava best practices
Java best practices
Godfrey Nolan
 
Counting sheep with Drones and AI
Counting sheep with Drones and AICounting sheep with Drones and AI
Counting sheep with Drones and AI
Godfrey Nolan
 
Writing Secure Mobile Apps for Drones
Writing Secure Mobile Apps for DronesWriting Secure Mobile Apps for Drones
Writing Secure Mobile Apps for Drones
Godfrey Nolan
 
Android Device Labs
Android Device LabsAndroid Device Labs
Android Device Labs
Godfrey Nolan
 
The Day We Infected Ourselves with Ransomware
The Day We Infected Ourselves with RansomwareThe Day We Infected Ourselves with Ransomware
The Day We Infected Ourselves with Ransomware
Godfrey Nolan
 
Agile Swift
Agile SwiftAgile Swift
Agile Swift
Godfrey Nolan
 
Android Refactoring
Android RefactoringAndroid Refactoring
Android Refactoring
Godfrey Nolan
 
From Maps to Apps the Future of Drone Technology
From Maps to Apps the Future of Drone TechnologyFrom Maps to Apps the Future of Drone Technology
From Maps to Apps the Future of Drone Technology
Godfrey Nolan
 
Tableau 10 and quickbooks
Tableau 10 and quickbooksTableau 10 and quickbooks
Tableau 10 and quickbooks
Godfrey Nolan
 
Network graphs in tableau
Network graphs in tableauNetwork graphs in tableau
Network graphs in tableau
Godfrey Nolan
 
Bulletproof
BulletproofBulletproof
Bulletproof
Godfrey Nolan
 

More from Godfrey Nolan (20)

Counting Cars with Drones
Counting Cars with DronesCounting Cars with Drones
Counting Cars with Drones
 
Customising QGroundControl
Customising QGroundControlCustomising QGroundControl
Customising QGroundControl
 
DJI Payload SDK
DJI Payload SDKDJI Payload SDK
DJI Payload SDK
 
Parrot Tutorials in Kotlin
Parrot Tutorials in KotlinParrot Tutorials in Kotlin
Parrot Tutorials in Kotlin
 
DJI Mobile SDK Tutorials in kotlin
DJI Mobile SDK Tutorials in kotlinDJI Mobile SDK Tutorials in kotlin
DJI Mobile SDK Tutorials in kotlin
 
Drone sdk showdown
Drone sdk showdownDrone sdk showdown
Drone sdk showdown
 
AI/ML in drones
AI/ML in dronesAI/ML in drones
AI/ML in drones
 
Getting started with tensor flow datasets
Getting started with tensor flow datasets Getting started with tensor flow datasets
Getting started with tensor flow datasets
 
Using ML to make your UI tests more robust
Using ML to make your UI tests more robustUsing ML to make your UI tests more robust
Using ML to make your UI tests more robust
 
Java best practices
Java best practicesJava best practices
Java best practices
 
Counting sheep with Drones and AI
Counting sheep with Drones and AICounting sheep with Drones and AI
Counting sheep with Drones and AI
 
Writing Secure Mobile Apps for Drones
Writing Secure Mobile Apps for DronesWriting Secure Mobile Apps for Drones
Writing Secure Mobile Apps for Drones
 
Android Device Labs
Android Device LabsAndroid Device Labs
Android Device Labs
 
The Day We Infected Ourselves with Ransomware
The Day We Infected Ourselves with RansomwareThe Day We Infected Ourselves with Ransomware
The Day We Infected Ourselves with Ransomware
 
Agile Swift
Agile SwiftAgile Swift
Agile Swift
 
Android Refactoring
Android RefactoringAndroid Refactoring
Android Refactoring
 
From Maps to Apps the Future of Drone Technology
From Maps to Apps the Future of Drone TechnologyFrom Maps to Apps the Future of Drone Technology
From Maps to Apps the Future of Drone Technology
 
Tableau 10 and quickbooks
Tableau 10 and quickbooksTableau 10 and quickbooks
Tableau 10 and quickbooks
 
Network graphs in tableau
Network graphs in tableauNetwork graphs in tableau
Network graphs in tableau
 
Bulletproof
BulletproofBulletproof
Bulletproof
 

Recently uploaded

20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
Matthew Sinclair
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Safe Software
 
Presentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of GermanyPresentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of Germany
innovationoecd
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
Tomaz Bratanic
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
Matthew Sinclair
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
How to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptxHow to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptx
danishmna97
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
Aftab Hussain
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
panagenda
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
panagenda
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
Zilliz
 
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
名前 です男
 
Full-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalizationFull-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalization
Zilliz
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
Neo4j
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
KAMESHS29
 
UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6
DianaGray10
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Paige Cruz
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 

Recently uploaded (20)

20240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 202420240607 QFM018 Elixir Reading List May 2024
20240607 QFM018 Elixir Reading List May 2024
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
 
Presentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of GermanyPresentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of Germany
 
GraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracyGraphRAG for Life Science to increase LLM accuracy
GraphRAG for Life Science to increase LLM accuracy
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
How to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptxHow to Get CNIC Information System with Paksim Ga.pptx
How to Get CNIC Information System with Paksim Ga.pptx
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
HCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAUHCL Notes and Domino License Cost Reduction in the World of DLAU
HCL Notes and Domino License Cost Reduction in the World of DLAU
 
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAUHCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
HCL Notes und Domino Lizenzkostenreduzierung in der Welt von DLAU
 
Building Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and MilvusBuilding Production Ready Search Pipelines with Spark and Milvus
Building Production Ready Search Pipelines with Spark and Milvus
 
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
みなさんこんにちはこれ何文字まで入るの?40文字以下不可とか本当に意味わからないけどこれ限界文字数書いてないからマジでやばい文字数いけるんじゃないの?えこ...
 
Full-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalizationFull-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalization
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
 
UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6UiPath Test Automation using UiPath Test Suite series, part 6
UiPath Test Automation using UiPath Test Suite series, part 6
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 

Android TDD

  • 2. AGENDAAGENDA Unit Testing intro Up and Running Unit testing 101 Tools of the trade Mocking TDD Espresso Adding TDD to existing projects
  • 3. UNIT TESTING INTROUNIT TESTING INTRO Hello World Benefits Android Testing Pyramid Activity Testing
  • 4. UNIT TESTING INTROUNIT TESTING INTRO Catch more mistakes Confidently make more changes Built in regression testing Extend the life of your codebase
  • 5. UNIT TESTING INTROUNIT TESTING INTRO public 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); }
  • 6.
  • 7.
  • 8.
  • 9.
  • 10. UP AND RUNNINGUP AND RUNNING Gradle version build.gradle changes Build Variant Sample app
  • 11.
  • 12. dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' }
  • 13.
  • 14.
  • 15. UNIT TESTING 101UNIT TESTING 101 Command line Setup and Teardown Grouping Parameters Assertions Code Coverage
  • 16. C:UsersgodfreyAndroidStudioProjectsBasicSample>gradlew test --continue Downloading https://services.gradle.org/distributions/gradle-2.2.1-all.zip ................................................................................ .................................................. Unzipping C:Usersgodfrey.gradlewrapperdistsgradle-2.2.1-all6dibv5rcnnqlfbq9klf8imrndngr Download https://jcenter.bintray.com/com/google/guava/guava/17.0/guava-17.0.jar Download https://jcenter.bintray.com/com/android/tools/lint/lint-api/24.2.3/lint-api-24.2.3.jar Download https://jcenter.bintray.com/org/ow2/asm/asm-analysis/5.0.3/asm-analysis-5.0.3.jar Download https://jcenter.bintray.com/com/android/tools/external/lombok/lombok-ast/0.2.3/lombok- :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
  • 17. 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; } }
  • 18. import android.test.suitebuilder.annotation.SmallTest; @SmallTest public void calculator_CorrectSub_Small_ReturnsTrue() { assertEquals(mCalculator.sub(4, 3),1,0); } android { //.... defaultConfig { //.... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArgument "size", "small" } }
  • 19. @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); } }
  • 20. UNIT TESTING 101UNIT TESTING 101 assertEquals assertTrue assertFalse assertNull assertNotNull assertSame assertNotSame assertThat fail
  • 21.
  • 22. TOOLS OF THE TRADETOOLS OF THE TRADE Hamcrest Mockito Robolectic Jenkins
  • 23. dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3' } @Test public void calculator_CorrectHamAdd_ReturnsTrue() { assertThat(both(greaterThan(6)).and(lessThan(8)), mCalculator.add(3, 4)); }
  • 24. dependencies { // Unit testing dependencies. testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' } @SmallTest 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); } }
  • 25. 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!")); } }
  • 26. @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_a } @Test public void listLoaded() throws Exception { assertThat("should be a dozen star signs", zodiacSigns.length, equalTo(listView.getCount()) } }
  • 27.
  • 28.
  • 29.
  • 31. 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);
  • 32. @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")); } }
  • 33. @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); } }
  • 34. @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_VOL new AudioHelper().maximizeVolume(audioManager); // verify with Mockito that setStreamVolume to 100 was called. Mockito.verify(audioManager).setStreamVolume(AudioManager.STREAM_RING, MAX_VOLUME, 0); } }
  • 35. @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")); } }
  • 36. TEST DRIVEN DEVELOPMENT (TDD)TEST DRIVEN DEVELOPMENT (TDD) Unit testing vs TDD Why TDD Sample app Lessons learned
  • 37. TEST DRIVEN DEVELOPMENTTEST DRIVEN DEVELOPMENT Write test first See it fail Write simplest possible solution to get test to pass Refactor Wash, Rinse, Repeat
  • 38. TEST DRIVEN DEVELOPMENTTEST DRIVEN DEVELOPMENT Built in regression testing Longer life for your codebase YAGNI feature development Red/Green/Refactor helps kill procrastination
  • 39. TDDTDD You can't TDD w/o unit testing TDD means writing the tests before the code TDD is more painless than classic unit testing UNIT TESTINGUNIT TESTING You can unit test w/o TDD Unit tests don't mandate when you write the tests Unit tests are often written at the end of a coding cycle
  • 40. SAMPLE APP - DAILY HOROSCOPESAMPLE APP - DAILY HOROSCOPE Display each star sign Display information about each star sign Display horoscope for star sign
  • 41.
  • 42. @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> @Test public 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)); }
  • 43. public static final Zodiac[] signs = { new Zodiac("Aries","Enterprising, Incisive, Spontaneous, Daring, Active, Courageous and Energetic, the Aries are the proverbial infants, guileless and op new Zodiac("Taurus","Known for being reliable, practical, ambitious and sensual, the people born under the Zodiac Sign Taurus have an eye for beauty.", " 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 b 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 ta 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") }; @Before public 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); } @Test public 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()); } }
  • 44. 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()); }
  • 45. LESSONS LEARNEDLESSONS LEARNED What worked No longer need emulator Not so much Android Activities don't work well with TDD Robolectric is your friend
  • 47.
  • 48.
  • 49. @RunWith(AndroidJUnit4.class) @LargeTest public 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))); } } @Test public void helloWorldButtonTest(){ onView(withId(R.id.button)) .perform(click()) .check(matches(isEnabled())); }
  • 50. @RunWith(AndroidJUnit4.class) @LargeTest public 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"))); } }
  • 51. EXISTING PROJECTSEXISTING PROJECTS Way more common Essential Steps Lessons Learned
  • 52. STEPSSTEPS Introduce Continuous Integration to build code Configure android projects for TDD Add minimal unit tests based on existing tests, add to CI Show team how to create unit tests Add testing code coverage metrics to CI, expect 5-10% Add Espresso tests Unit test new features or sprouts, mock existing objects Wrap or ring fence existing code, remove unused code Refactor wrapped code to get code coverage to 60-70%
  • 53. 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; } } }
  • 54.
  • 55. 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&charac 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","srcmain" property "sonar.tests", "srctest" property "sonar.jacoco.reportPath", "buildjacocojacocoTest.exec" property "sonar.java.binaries", "build" property "sonar.dynamicAnalysis", "resuseReports" } }
  • 56. LESSONS LEARNEDLESSONS LEARNED Look 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