I使用Spring Android Rest Template执行HTTP请求。我是这样测试的:
package com.mnubo.platform.android.sdk.internal.user.services.impl;
import com.mnubo.platform.android.sdk.models.users.User;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
@RunWith(RobolectricTestRunner.class)
@Config(emulateSdk = 18)
public class DummyTest {
private MyApiImpl myApi;
private MockRestServiceServer myapiMockServer;
@Before
public void setUp() throws Exception {
myApi = new MyApiImpl("ACCESS_TOKEN");//break here
myapiMockServer = MockRestServiceServer.createServer(myApi.getRestTemplate());
}
@Test
public void testRestClient() throws Exception {
final User expectedUser = new User();
expectedUser.setUsername("test");
expectedUser.setLastname("lastname");
myapiMockServer.expect(requestTo("http://my-service.com/user/test"))
.andExpect(method(GET))
.andRespond(withSuccess(this.convertToJsonString(expectedUser), APPLICATION_JSON_UTF8));
User user = myApi.userOperations().getUser("test");
userMockServer.verify();
}
}
使用RoboelectricTestRunner,所有这些都能正常工作。但是,昨天安卓工作室更新了,并要求我将构建工具版本更新到1.1.0。(com.android.tools.build:gradle:1.1.0).
这个版本现在包括对单元测试的支持。看见https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support
问题是:我不能再创建MyApiImpl
了,因为它创建了一个RestTemplate。这个RestTemplate使用org.springframework.http.client.HttpComponentsClientHttpRequestFactory,在这个类构造函数中,使用org.apache.http包中的方法。
这些方法引发了一个异常:例如:Method getSocketFactory in org.apache.http.conn.scheme.PlainSocketFactory
好吧,我使用PowerMock成功地模拟了getSocketFactory,但随后我不得不模拟SchemeRegistry的register
方法,该方法只能从构造函数(源代码)本地访问。
所以我放弃了嘲笑RestTemplate内部发生的所有恶作剧,而是决定直接嘲笑RestTemplate。
我需要帮助来正确地模拟它,以便MockRestServiceServer
仍然可以在我的测试中使用,因为现在myapiMockServer.verify()
断言失败了。
更新我仍然无法使用MockRestServiceServer
来测试我的Api,但我设法使用RestTemplate
的模拟来测试每个单独的OperationImpl
,如下所示:
public class DummyOperationTest {
private DummyOperation dummyOperation;
private RestTemplate mockedRestTemplate = mock(RestTemplate.class);
@Before
public void setUp() throws Exception {
super.setUp();
dummyOperation = new DummyOperationImpl(PLATFORM_BASE_URL, mockedRestTemplate);
}
@Test
public void test() throws Exception {
String calledUrl = PLATFORM_BASE_URL + "/objects?update_if_exists=true";
when(mockedRestTemplate.postForObject(calledUrl, expectedSmartObject, SmartObject.class)).thenReturn(expectedSmartObject);
smartObjectService.create(expectedSmartObject, true);
verify(mockedRestTemplate, atMost(1)).postForObject(calledUrl, expectedSmartObject, SmartObject.class);
}
}
不幸的是,这仍然不能测试整个请求的执行。我无法验证oAuth身份验证是否正确添加到标头中,或者服务器响应的转换是否正确。
好吧,这是一个嘲弄的问题。我不是每件事都理解正确,但我终于把它弄明白了。下面是一个示例,展示了如何进行:
package com.mnubo.platform.android.sdk.internal;
import android.util.Log;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RunWith(PowerMockRunner.class)
@PrepareForTest({
Log.class,
PlainSocketFactory.class,
SSLSocketFactory.class,
HttpComponentsClientHttpRequestFactory.class,
ConnManagerParams.class,
AbstractHttpClient.class
})
public class DummyTest {
private final String USER_ACCESS_TOKEN = "user_token";
private final PlainSocketFactory mockedPlainSocketFactory = mock(PlainSocketFactory.class);
private final SSLSocketFactory mockedSSLSocketFactory = mock(SSLSocketFactory.class);
private final SchemeRegistry mockedSchemeRegistry = mock(SchemeRegistry.class);
private final DefaultHttpClient mockedHttpClient = mock(DefaultHttpClient.class);
private final HttpParams mockedHttpParams = mock(HttpParams.class);
private DummyApiImpl dummyApi = new DummyApiImpl(USER_ACCESS_TOKEN);
protected MockRestServiceServer mockServer;
@Before
public void setUp() throws Exception {
mockStatic(Log.class);
mockStatic(PlainSocketFactory.class);
mockStatic(SchemeRegistry.class);
mockStatic(SSLSocketFactory.class);
mockStatic(ConnManagerParams.class);
whenNew(SchemeRegistry.class).withAnyArguments().thenReturn(mockedSchemeRegistry);
whenNew(DefaultHttpClient.class).withAnyArguments().thenReturn(mockedHttpClient);
when(PlainSocketFactory.getSocketFactory()).thenReturn(mockedPlainSocketFactory);
when(SSLSocketFactory.getSocketFactory()).thenReturn(mockedSSLSocketFactory);
when(mockedHttpClient.getParams()).thenReturn(mockedHttpParams);
mockServer = MockRestServiceServer.createServer(dummyApi.getRestTemplate());
}
@Test
public void doOperationTest() throws Exception {
final User testUser = new User();
mockUserServiceServer.expect(requestTo(expectedUrl("/users/test")))
.andExpect(method(POST))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andRespond(withSuccess());
dummyApi.userOperations().createUser("test", testUser);
mockUserServiceServer.verify();
}
}
调用Api的构造函数时出现问题。这个类扩展了AbstractOAuth2ApiBinding
,它的构造函数创建了一个RestTemplate
。这个对象需要多个级别的嘲讽。
一旦您模拟了创建RestTemplate
所需的元素,剩下的就很容易了,这要归功于模拟服务器!