RestAdapter.Builder
はsetClient()
できるので、テスト時はそこにテスト用のClientをセットしてあげる。そのテスト用のClientをMockClientとしました。
本当は、Clientをわざわざ作るのがめんどくさいなと思って作らない方針で行こうとしたんですが、基盤チームの人にsetClient
する方法があるでと改めて指摘されたあと考えなおして、Clientを作るのはだるいけど作ったほうが後々楽でわかりやすいなと思えてきたので作りました。
流れとしては、以下のような感じを想定しています。
@Before
でMockClientをセットする(正確にはRestAdapterをセットする)
@Test
で なにをどうモックするか指定する
@After
で指定したモックをクリアする
public class MockClientTest {
MockClient mMockClient = new MockClient();
@Before
public void setUp() throws Exception {
ApiClient.getInstance().setRestAdapter(RestAdapterFactory.newMocking(mMockClient));
}
@After
public void tearDown() throws Exception {
mMockClient.clear();
}
@Test
public void testMock() throws Exception {
mMockClient.mock("/v1/reviews/100.json").to(200, "reviews/ok.json");
Review review = ApiClient.getInstance().create(ReviewsService.class).fetch(100);
assertThat(review.getStar()).isEqualTo(5);
}
@Test
public void testMock_route() throws Exception {
MockClient.Route route = mMockClient.mock("/v1/reviews/100.json").to(200, "reviews/ok.json");
ApiClient.getInstance().create(ReviewsService.class).fetch(100);
assertThat(route.called()).isEqualTo(true);
assertThat(route.times()).isEqualTo(1);
}
@Test
public void testMock_regex() throws Exception {
MockClient.Route route = mMockClient.mock("/v1/reviews/\\d.json").to(200, "reviews/ok.json");
ApiClient.getInstance().create(ReviewsService.class).fetch(100);
assertThat(route.called()).isEqualTo(true);
}
}
public class MockClient implements Client {
List<Route> mRoutes = new ArrayList<>();
Route notFoundRoute = new Route("404").to(404, "response/not_found.json");
@Override
public Response execute(Request request) throws IOException {
for (Route route : mRoutes) {
Response response = route.match(request.getUrl());
if (response == null) {
continue;
}
return response;
}
return notFoundRoute.newResponse(request.getUrl());
}
public Route mock(String urlPath) {
Route route = new Route(urlPath);
mRoutes.add(route);
return route;
}
public void clear() {
mRoutes = new ArrayList<>();
}
public class Route {
Pattern mUrlPathRegex;
int mStatus;
int mTimes;
String mJsonPath;
Route(String urlPath) {
mUrlPathRegex = Pattern.compile(urlPath);
}
public Route to(int status, String jsonPath) {
mStatus = status;
mJsonPath = jsonPath;
return this;
}
public int times() {
return mTimes;
}
public boolean called() {
return mTimes >= 1;
}
private Response match(String url) throws IOException {
Matcher matcher = mUrlPathRegex.matcher(url);
if (!matcher.find()) {
return null;
}
return newResponse(url);
}
private String getContentType() {
return "application/json";
}
private Response newResponse(String url) throws IOException {
byte[] body = TestUtils.getAssetFileInBytes(mJsonPath);
mTimes++;
return new Response(
url,
mStatus,
"mocked-response",
new ArrayList<Header>(),
new TypedByteArray(getContentType(), body)
);
}
}
}
テストが並列で走ることとかはまだ考えてないです。
Clientを作るのめんどいと思ったけど、案外すぐ出来たので、そういうの良くないなって思いました。