我是Java单元测试新手,我听说Mockito框架非常适合测试目的。
我已经开发了一个 REST 服务器(CRUD 方法),现在我想测试它,但我不知道怎么做?
更我不知道这个测试程序应该如何开始。我的服务器应该在本地主机上工作,然后调用该网址(例如本地主机:8888)?
这是我到目前为止尝试的,但我很确定这不是正确的方法。
@Test
public void testInitialize() {
RESTfulGeneric rest = mock(RESTfulGeneric.class);
ResponseBuilder builder = Response.status(Response.Status.OK);
builder = Response.status(Response.Status.OK).entity(
"Your schema was succesfully created!");
when(rest.initialize(DatabaseSchema)).thenReturn(builder.build());
String result = rest.initialize(DatabaseSchema).getEntity().toString();
System.out.println("Here: " + result);
assertEquals("Your schema was succesfully created!", result);
}
这是initialize
方法的代码。
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("/initialize")
public Response initialize(String DatabaseSchema) {
/** Set the LogLevel to Info, severe, warning and info will be written */
LOGGER.setLevel(Level.INFO);
ResponseBuilder builder = Response.status(Response.Status.OK);
LOGGER.info("POST/initialize - Initialize the " + user.getUserEmail()
+ " namespace with a database schema.");
/** Get a handle on the datastore itself */
DatastoreService datastore = DatastoreServiceFactory
.getDatastoreService();
datastore.put(dbSchema);
builder = Response.status(Response.Status.OK).entity(
"Your schema was succesfully created!");
/** Send response */
return builder.build();
}
在这个测试用例中,我想将一个 Json 字符串发送到服务器(POST)。如果一切顺利,那么服务器应该回复"您的架构已成功创建!
有人可以帮我吗?
好的。因此,该方法的协定如下:将输入字符串解析为 JSON,如果无效,则发回BAD_REQUEST
。如果有效,请在datastore
中创建一个具有各种属性(您知道它们)的实体,然后发回OK
。
并且您需要验证该方法是否履行了此合同。
莫米托在这里有什么帮助?好吧,如果您在没有 Mockito 的情况下测试此方法,则需要一个真实的DataStoreService
,并且您需要验证实体是否已在此真实DataStoreService
中正确创建。这就是你的测试不再是单元测试的地方,也是它太复杂而无法测试、太长和太难运行的地方,因为它需要一个复杂的环境。
Mockito可以通过模拟对DataStoreService
的依赖来提供帮助:您可以创建一个DataStoreService
的模拟,并在测试中调用initialize()
方法时验证该模拟是否确实使用适当的实体参数调用。
为此,您需要能够将DataStoreService
注入到被测对象中。它可以像通过以下方式重构对象一样简单:
public class MyRestService {
private DataStoreService dataStoreService;
// constructor used on the server
public MyRestService() {
this.dataStoreService = DatastoreServiceFactory.getDatastoreService();
}
// constructor used by the unit tests
public MyRestService(DataStoreService dataStoreService) {
this.dataStoreService = dataStoreService;
}
public Response initialize(String DatabaseSchema) {
...
// use this.dataStoreService instead of datastore
}
}
现在,在您的测试方法中,您可以执行以下操作:
@Test
public void testInitializeWithGoodInput() {
DataStoreService mockDataStoreService = mock(DataStoreService.class);
MyRestService service = new MyRestService(mockDataStoreService);
String goodInput = "...";
Response response = service.initialize(goodInput);
assertEquals(Response.Status.OK, response.getStatus());
ArgumentCaptor<Entity> argument = ArgumentCaptor.forClass(Entity.class);
verify(mock).put(argument.capture());
assertEquals("the correct kind", argument.getValue().getKind());
// ... other assertions
}
你所说的听起来更像是集成测试,而Mockito(或任何其他模拟框架)对你来说没有多大用处。
如果你想对你编写的代码进行单元测试,Mockito当然是一个有用的工具。
我建议你阅读更多关于模拟/单元测试的信息,以及它应该在哪些情况下使用。
Mockito(通常)用于测试部分代码;例如,如果您正在使用REST服务,但不想进行全栈测试,则可以模拟连接到REST服务的服务,从而允许您精确且一致地测试特定行为。
要在不访问数据库的情况下测试 REST 服务的内部部分(例如,特定的服务方法),您可以模拟数据库子系统,只允许测试服务内部,而不涉及数据库。此测试属于 REST 服务模块,而不是客户端。
要测试 REST 服务本身,您需要使用实际的客户端库,从而创建一个全栈集成测试。Mockito可以在这里用于模拟与REST服务消费无关的客户端部分。
最好的方法是使用 wiremock 添加以下依赖项 com.github.tomakehurst wiremock 2.4.1 org.igniterealtime.smack smack-core 4.0.6
定义和使用线模,如下所示
@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);
String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json").withBody(response)));
我同意这不是单元测试,而是集成测试,无论如何,您宁愿看看球衣和嵌入式灰熊服务器测试。总而言之,这段代码在 localhost:8888 启动灰熊服务器(也可以启动 datbase),然后设置客户端的球衣客户端并发送一个 POST 请求,该请求应该测试响应。这是一个集成,因为您同时测试服务器和数据库,但是您可以使用 mockito 来模拟数据库,但这取决于您的服务器和数据库的绑定程度。
(使用球衣 1.11 和灰熊 2.2 进行测试)
@BeforeClass
public static void setUpClass() throws Exception {
// starts grizzly
Starter.start_grizzly(true);
Thread.sleep(4000);
}
@Before
public void setUp() throws Exception {
client = new Client();
webResource = client.resource("http://localhost:8888");
}
@Test
public void testPostSchemaDatabase() throws Exception {
{
String DatabaseSchema = "{ database_schema : {...}}";
logger.info("REST client configured to send: " + DatabaseSchema);
ClientResponse response =
webResource
.path("/initialize")
.type("application/json")
.post(ClientResponse.class, DatabaseSchema);
//wait for the server to process
Thread.sleep(2000);
assertEquals(response.getStatus(), 204);
//test the response
}
}
@After
public void after() throws JSONException
{
//probably you want to delete the schema at database and stop the server
}