Android Espresso UI Test with mocked Retrofit API

一世执手 提交于 2019-12-06 09:56:53

问题


I'm creating UI Tests for my Android app using Espresso.

I have a login screen which contains just two Fields (user and password) and a Button.

I'm using Retrofit but I want to mock the API response so I've created a RetrofitMockClient.

For example, in the next Test I type a wrong password intentionally to check that an error message is displayed.

@Test
public void loginFailed() {
    onView(withId(R.id.login_user_name)).perform(typeText("myUser"));
    onView(withId(R.id.login_user_password)).perform(typeText("wrongPassword"), closeSoftKeyboard());

    onView(withId(R.id.login_button)).perform(click());

    // Failed login shows error message.
    onView(withText(R.string.login_error_message)).check(matches(isDisplayed()));
}

The problem is that when the click is performed, that triggers a Retrofit API call, the one I want to mock.

Any ideas about it? I'm also a bit confused about UI Test / Unit Tests and if this Test makes sense or not. I just want to check that the error message is displayed when the API returns an error during the login process.

Thanks in advance.

Edit:

I've introduced MockWebServer as the user fzxt suggested, using the RestServiceTestHelper class provided in the example.

Now my Test looks like this:

@RunWith(AndroidJUnit4.class)
public class LoginUIAndroidTest {

    private static MockWebServer mockWebServer;


    @Rule
    public ActivityTestRule<LoginActivity> mActivityRule = new IntentsTestRule<>(LoginActivity.class,
            true,    // initialTouchMode
            false); // launchActivity - false so we could customize the intent




    @BeforeClass
    public static void startMockServer() throws Exception {
        mockWebServer = new MockWebServer();
        mockWebServer.start();
        mockWebServer.url("/");
    }


    @AfterClass
    public static void shutdownMockServer() throws Exception {
        mockWebServer.shutdown();
    }
    @Test
    public void testLoginSuccess() throws Exception {

        String fileName = "quote_login_200_ok.json";
        mockWebServer.enqueue(new MockResponse()
                .setResponseCode(200)
                .setBody(RestServiceTestHelper.getStringFromFile(getInstrumentation().getContext(), fileName)));


        onView(withId(R.id.login_user_name)).perform(typeText("user"));
        onView(withId(R.id.login_user_password)).perform(typeText("password"), closeSoftKeyboard());

        onView(withId(R.id.login_button)).perform(click());

        // Successful login moves user to next screen.
        intended(hasComponent(new ComponentName(getInstrumentation().getTargetContext(), ProjekteActivity.class)));
    }

RestServiceTestHelper.java:

public class RestServiceTestHelper {

    public static String convertStreamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        reader.close();
        return sb.toString();
    }

    public static String getStringFromFile(Context context, String filePath) throws Exception {
        final InputStream stream = context.getResources().getAssets().open(filePath);

        String ret = convertStreamToString(stream);
        //Make sure you close all streams.
        stream.close();
        return ret;
    }
}

Here is my LoginActivity:

 public class LoginActivity extends Activity {

        ...

        @OnClick(R.id.login_button)
        public void loginUser(final View view) {
            final MyRetrofitService service = createRetrofitService();

            service.login(new AuthenticationLoginDTO(email, password, deviceUUID), new Callback < AuthenticationDTO > () {
                @Override
                public void success(final AuthenticationDTO authenticationDTO, final Response response) {}

                @Override
                public void failure(final RetrofitError retrofitError) {}
            });

        }



        protected MyRetrofitService createRetrofitService() {
            final RequestInterceptor requestInterceptor = new RequestInterceptor() {
                @Override
                public void intercept(final RequestInterceptor.RequestFacade request) {
                    request.addHeader(getString(R.string.header_api_key), getString(R.string.api_key));
                    request.addHeader(getString(R.string.header_context), getString(R.string.context));
                }
            };
            final Gson dateTimeConverter = new GsonBuilder()
                .registerTypeAdapter(DateTime.class, new DateTimeConverter())
                .create();
            final RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint("/")
                .setConverter(new GsonConverter(dateTimeConverter))
                .setRequestInterceptor(requestInterceptor)
                .setExecutors(AsyncTask.THREAD_POOL_EXECUTOR, new MainThreadExecutor())
                .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog(Config.TAG))
                .build();
            return restAdapter.create(MyRetrofitService.class);
        }
    }

And the Retrofit Interface:

public interface NGOPService {

    String AUTHENTICATION_ENDPOINT = "";

    @POST(AUTHENTICATION_ENDPOINT)
    void login(@Body AuthenticationLoginDTO authRequest, Callback < AuthenticationDTO > callback);

}

I'm missing something because when Espresso performs the click action, the real Api is being called instead of MockWebServer.

onView(withId(R.id.login_button)).perform(click());

I've seen some examples using Dagger 2 and Retrofit 2.0 but unfortunately, I have to use Retrofit 1.9 for now.

Thanks in advance.

来源:https://stackoverflow.com/questions/44904673/android-espresso-ui-test-with-mocked-retrofit-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!