7 stages of unit testing
-
Upload
jorge-ortiz -
Category
Technology
-
view
825 -
download
1
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