Parameterized testing with Robolectric

In our current project we are using Robolectric when writing unit tests for our Android application and it has been working out really well. Recently I needed to write a test case that performed an operation several times, but with different test data, and assert that the correct actions occurred depending on the data.

JUnit has an easy to use option for this called Parameterized testing where one defines the test data and then uses the Parameterized test runner to execute the test. This will create one instance of the test class for each element of test data with the data being passed to the constructor.

As it turns out, Robolectric has a ParameterizedRobolectricTestRunner, which is exactly the same thing (but slightly tweaked to conform with Robolectric) and worked very well for my test that verifies the app behaviour for different error codes received from an external service provider.

@RunWith(ParameterizedRobolectricTestRunner.class)
public class ContactServiceTest {

    @ParameterizedRobolectricTestRunner.Parameters(name = "ErrorCode = {0}")
    public static Collection data() {
        return Arrays.asList(new Object[][]{
                {105, 105_ERROR_MSG},
                {113, 113_ERROR_MSG},
                {114, 114_ERROR_MSG},
                {134, 134_ERROR_MSG},
                {137, 137_ERROR_MSG},
                {999, DEFAULT_ERROR_MSG} // Bogus error code
        });
    }

    private int errorCode;
    private String expectedErrorMsg;

    public ContactServiceTest(int errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.expectedErrorMsg = errorMsg;
    }

    @Test
    public void when_known_error_code_is_received_from_service_correct_error_msg_is_displayed_to_user() {
        // HTTP response from service contains defined error code
        Robolectric.addPendingHttpResponse(HttpStatus.SC_OK, buildFakeServiceResponse(errorCode)); 
        // Contact the service
        mService.contactService();
        // Use awaitility to wait until error message is displayed to user
        // then assert that the error message is correct
        await().until(getDisplayedErrorMsg(), is(expectedErrorMsg));
    }

This will execute the test case six times, once for each element of test data, and compare the displayed error message to the error message defined for the specific error code. Each test run will be treated as its own test case as the report is created.
Adding the name parameter to the Parameters annotation will tidy up the test results. As the results for the test run are displayed, the name of the test case will in this case be

when_known_error_code_is_received_from_service_correct_error_msg_is_displayed_to_user[ErrorCode = 105]
when_known_error_code_is_received_from_service_correct_error_msg_is_displayed_to_user[ErrorCode = 113]
when_known_error_code_is_received_from_service_correct_error_msg_is_displayed_to_user[ErrorCode = 114]
...

For further inspiration checkout JUnit Theories and also junit-quickcheck, a great way to generate the test data in your JUnit (and therefore also Robolectric) test, instead of defining it yourself.

This Post Has 2 Comments

  1. Robert Evans

    I find when I use a data type such as an enum as a test argument, I get this error:
    java.lang.IllegalArgumentException: argument type mismatch

    My code, modified from yours:

    @RunWith(ParameterizedRobolectricTestRunner.class)
    public final
    class ContactServiceTest {

    public
    enum ErrorCode {
    AAA,
    BBB,
    CCC
    }

    @ParameterizedRobolectricTestRunner.Parameters(name = “ErrorCode = {0}”)
    public static
    Collection data() {
    return Arrays.asList(new Object[][]{
    // {105, “105_ERROR_MSG”},
    // {113, “113_ERROR_MSG”},
    // {999, “DEFAULT_ERROR_MSG”}

    {ErrorCode.AAA, “105_ERROR_MSG”},
    {ErrorCode.BBB, “113_ERROR_MSG”},
    {ErrorCode.CCC, “DEFAULT_ERROR_MSG”}
    });
    }

    private int errorCode;

    private String expectedErrorMsg;

    public
    ContactServiceTest(ErrorCode errorCode, String errorMsg) {
    // this.errorCode = errorCode;
    // this.expectedErrorMsg = errorMsg;
    }

    @Test
    public
    void testRobolectricTestWithData() {

    }
    }

Leave a Reply