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