As an Android developer you are already familiar with writing Unit and UI tests. This post is about "How to share the code and resources between your UI and unit tests?". I'd used "Appointments List" as an example scenario for this post.
androidTest/java/com/example/AppointmentScreenTest.java
Existing Setup
For UI testing, I'd have list of appointments API response in JSON format as "appointments.json" in res directory. The .json file is is converted to AppointmentsListResponse using JsonParser.
AppointmentsListResponse getFakeAppointmentsResponse() { InputStream in = getInstrumentation().getContext() .getResources() .openRawResource(com.example.android.internal.test.R.raw.appointments); AppointmentsListResponse fakeResponse = new JsonParser(in).parseTo(AppointmentsListResponse.class); }
androidTest/java/com/example/JsonParser.java
public class JsonParser String mStringToParse; private static Gson mGson = new Gson(); public JsonParser(String parseString) { mStringToParse = parseString; } public JsonParser(InputStream is) { try (java.util.Scanner s = new java.util.Scanner(is)) { mStringToParse = s.useDelimiter("\\A").hasNext() ? s.next() : ""; } } public <T> T parseTo(Class<T> targetClass) { try { return targetClass.cast(mGson.fromJson(mStringToParse, targetClass)); } catch (ClassCastException e) { return null; } } }
For presenter tests, I'd created fake response manually like shown below:
test/java/com/example/AppointmentPresenterTest.java
public static AppointmentsListResponse getFakeAppointmentListResponse() { AppointmentsListResponse response = new AppointmentsListResponse(); response.setAppointments(getFakeAppointmentsList()); response.setPatients(getFakePatientList()); response.setDoctors(getFakeDoctorsList()); return response; }
What's the problem?
- UI tests use real world data for testing but for unit tests we need to construct the data manually.
- Requires 'Setter' methods for response class to facilitate test data creation.
- Code repetition in both UI and unit tests.
What's the solution?
Step 1: Create a shared test folder called testShared under app.
Step 2: Add a new File | New | Folder | Java Folder
Step 3: Add a new File | New | Folder | Java Resources Folder
Step 4: Add the test resources in the newly created resources directory.
e.g., For this scenario I have added "appointments.json" file.
Step 5: Create a SharedTestHelper class which provides the test data for both UI & unit tests.
Step 6: In your app/build.gradle, add the following lines
android.sourceSets { test { java.srcDirs += "$projectDir/src/testShared/java" resources.srcDirs += "$projectDir/src/testShared/resources" } androidTest { java.srcDirs += "$projectDir/src/testShared/java" resources.srcDirs += "$projectDir/src/testShared/resources" } }
testShared/java/com/example/SharedTestHelper.java
public final class SharedTestHelper { private final Context mContext; private final ClassLoader mClassLoader; public SharedTestHelper() { this(null); } public SharedTestHelper(Context context) { mContext = context; if (context == null) { mClassLoader = ClassLoader.getSystemClassLoader(); } else { mClassLoader = context.getClassLoader(); } } public AppointmentsListResponse getFakeAppointmentsResponse() { InputStream in = mClassLoader.getResourceAsStream("appointments.json"); AppointmentsListResponse fakeResponse = new JsonParser(in).parseTo(AppointmentsListResponse.class); return fakeResponse; } }Unit Tests - Use JVM's Built-In classloader. UI Tests - Use Dalvik's classloader which can be accessed via instrumentation context.
Usage
UI Tests
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); new SharedTestHelper(instrumentation.getTargetContext()).getFakeAppointmentsResponse();
Unit Tests
new SharedTestHelper().getFakeAppointmentsResponse();As you see we are reusing the same test data for both UI and unit tests, no extra setter methods. The entire mock data generation is shared between both tests, so there is no code repetition.
Thanks for @PavelDudka's awesome post Android Test tricks - Sharing code between UI & unit tests
Comments
Post a Comment