7 stages of unit testing

Post on 18-Jul-2015

825 views 1 download

Tags:

Transcript of 7 stages of unit testing

7 Stages of Unit Testing in Android

Jorge D. Ortiz Fuentes @jdortiz

#7SUnitTest

A POWWAU production

#7SUnitTest

#7SUnitTest

Daring as a fool

★Speaking from the shoulders of giants. Look up here!

★This is not UnitTesting 101, but complementary to it

1.

Shock & Disbelief

But my code is always awesome!

#7SUnitTest

Unit Tests★Prove correctness of different aspects of the

public interface.

• Prove instead of intuition

• Define contract and assumptions

• Document the code

• Easier refactoring or change

★Reusable code = code + tests

#7SUnitTest

Use Unit Testing Incrementally

★You don’t have to write every unit test

★Start with the classes that take care of the logic

• If mixed apply SOLID

★The easier entry point are bugs

2.

Denial

C’mon, it takes ages to write

tests!

Time writing tests < Time debugging

#7SUnitTest

Good & Bad News★Most things are already available in Android Studio

★Projects are created with

• test package

• ApplicationTest class

★ It requires some tweaking

★Google docs are for Eclipse

• modules instead of projects

• different UI…

OTS functionality

JUnit

AssertTestCase

AndroidTestCase

TestSuite

ApplicationTestCase

InstrumentationTestCase

ActivityInstrumentationTestCase2

junit.framework

Run from IDE

Run in (V)Device

3.

Anger

How do I write those f***ing

tests?

#7SUnitTest

Types of Unit Tests

★Test return value

★Test state

★Test behavior

public class TaskTests extends TestCase { final static String TASK_NAME = "First task"; final static String TASK_DONE_STRING = "First task: Done"; final static String TASK_NOT_DONE_STRING = "First task: NOT done"; Task mTask; @Override public void setUp() throws Exception { super.setUp(); mTask = new Task(); } @Override public void tearDown() throws Exception { super.tearDown(); mTask = null; } public void testDoneStatusIsDisplayedProperly() throws Exception { mTask.setName(TASK_NAME); mTask.setDone(true); String taskString = mTask.toString(); assertEquals("String must be \"taskname: Done\"", TASK_DONE_STRING, taskString); } public void testNotDoneStatusIsDisplayedProperly() throws Exception { mTask.setName(TASK_NAME); mTask.setDone(false); assertEquals("String must be \"taskname: NOT done\"", TASK_NOT_DONE_STRING, mTask.toString()); } }

Examplepublic class Task { private String mName; private Boolean mDone; public String getName() { return mName; } public void setName(String name) { mName = name; } } public Boolean getDone() { return mDone; } public void setDone(Boolean done) { mDone = done; } @Override public String toString() { return mName + ": " + (mDone?"Done":"NOT done"); } }

public class ModelAndLogicTestSuite extends TestSuite { public static Test suite() { return new TestSuiteBuilder(ModelAndLogicTestSuite.class) .includePackages(“com.powwau.app.interactor”, “com.powwau.app.data”) .build(); }

public ModelAndLogicTestSuite() { super(); } }

Running more than one TestCase

public class FullTestSuite extends TestSuite { public static Test suite() { return new TestSuiteBuilder(FullTestSuite.class) .includeAllPackagesUnderHere() .build(); }

public FullTestSuite() { super(); } }

Instrumentation Testspublic class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { Activity mActivity; public MainActivityUnitTest() { super(MainActivity.class); } @Override public void setUp() throws Exception { super.setUp(); mActivity = getActivity(); } public void testHelloIsDisplayed() throws Exception { onView(withText(“Hello world!")).check(ViewAssertions.matches(isDisplayed())); } public void testSalutationChangesWithButtonClick() throws Exception { onView(withText("Touch Me")).perform(click()); onView(withText("Bye bye, Moon!”)).check(ViewAssertions.matches(isDisplayed())); } }

4.

Bargain

Ok, I’ll write some tests, but

make my life simpler

#7SUnitTest

Dependency Injection

★Control behavior of the dependencies

• Constructor

• Method overwriting

• Property injection:Lazy instantiation

★Or use a DI framework: Dagger 2

Dependency Injection for Poor Developers

public void preserveUserName(String name) { SharedPreferences prefs = getPreferences(“appPrefs”, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString(USER_NAME_KEY, name); editor.commit(); }

Dependency Injection for Poor Developers

public void preserveUserName(String name) { SharedPreferences prefs = getAppPrefs(); SharedPreferences.Editor editor = prefs.edit(); editor.putString(USER_NAME_KEY, name); editor.commit(); }

SharedPreferences getAppPrefs() { if (mAppPrefs == null) { mAppPrefs = getPreferences(“appPrefs”, Context.MODE_PRIVATE); } return mAppPrefs; }

DataRepo dataRepoMock = mock(DataRepo.class); when(dataRepoMock.existsObjWithId(23)).thenReturn(true); verify(dataRepoMock).deleteObjWithId(23);

Simulating and Testing Behavior

★Stubs & Mocks

• Both are fake objects

• Stubs provide desired responses to the SUT

• Mocks also expect certain behaviors

5.

Guilt

But this ain’t state of the art

#7SUnitTest

Problems so far

★Tests run in (v)device: slow

★Unreliable behavior:

• Command line

• Integration with mocking

6.

Depression

My tests are never good

enough!

#7SUnitTest

Use JUnit 4★Don’t extend TestCase

★Don’t start with test (@Test instead)

★@Before & @After instead of setUp and tearDown. Also for the class

★@ignore

★Exceptions & timeouts (@Test params)

★@Theory & @DataPoints

Gradle configsourceSets { test.setRoot(“src/test”) } dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.9.5' androidTestCompile 'junit:junit:4.12' androidTestCompile 'org.mockito:mockito-core:1.9.5' }

How to Use JUnit 4

#7SUnitTest

CLI-aware Also

★ ./gradlew check

• lint

• test

★Open app/build/reports/tests/debug/index.html

7.

Acceptance & Hope

No tests, no fun

#7SUnitTest

Evolve

★Clean Architecture

★TDD

★Functional tests :monkeyrunner

★CI (Jenkins)

Thank you!

@jdortiz #7SUnitTest