My experiences on Unit Testing in the Android environment. I hope they are useful to you too.
A brief tour about how to make Android Studio run your unit tests (logical and instrumentation) and how to start creating tests for your app.
#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
#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…
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()); } }
Example
public 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(ModelAndLogicTestS
uite.class)
.includePackages(“c
om.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.cl
ass)
.includeAllPackag
esUnderHere()
.build();
}
public FullTestSuite() {
super();
}
}
Instrumentation Tests
public 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()));
}
}
#7SUnitTest
Dependency Injection
★ Control behavior of the dependencies
• Constructor
• Method overwriting
• Property injection:Lazy instantiation
★ Or use a DI framework: Dagger 2
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
#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