SpringREST-如何检索返回资源的id?

SpringREST-如何检索返回资源的id?,spring,rest,Spring,Rest,我有一个集成测试是成功的,但我想添加到它 @Test public void testAdd() throws Exception { HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + PASSWORD); this.mockMvc.perform( post("/admin").headers(httpHeaders) .content

我有一个集成测试是成功的,但我想添加到它

@Test
public void testAdd() throws Exception {
    HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + PASSWORD);

    this.mockMvc.perform(
        post("/admin").headers(httpHeaders)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .content("{ \"firstname\" : \"" + admin0.getFirstname() + "\", \"lastname\" : \"" + admin0.getLastname() + "\", \"email\" : \"" + admin0.getEmail() + "\", \"login\" : \"" + admin0.getLogin() + "\", \"password\" : \"" + admin0.getPassword() + "\", \"passwordSalt\" : \"" + admin0.getPasswordSalt() + "\" }")
    ).andDo(print())
    .andExpect(status().isCreated())
    .andExpect(jsonPath("$.firstname").value(admin0.getFirstname()))
    .andExpect(jsonPath("$.lastname").value(admin0.getLastname()))
    .andExpect(jsonPath("$.email").value(admin0.getEmail()))
    .andExpect(jsonPath("$.login").value(admin0.getLogin()))
    .andExpect(jsonPath("$.password").value(admin0.getPassword()))
    .andExpect(jsonPath("$.passwordSalt").value(admin0.getPasswordSalt()))
    .andExpect(header().string("Location", Matchers.containsString("/admin/")))
    .andReturn();
}
例如,我可以对新创建的资源发送GET请求

然后我可以对它执行一个删除请求,并再次以一个GET结束

这是一个合理的集成测试场景吗

为此,我需要检索所创建资源的id

有办法吗

谢谢大家!


Stephane

根据您要测试的内容,我可以考虑几个选项

最简单和最快的方法是模拟您的用户服务/存储库,或者任何返回用户详细信息的方法。使用Mockito,您可以获得如下代码:

// At start of test, we have an admin user in the mock database
when(userRepo.findOne(1)).thenReturn(myNewAdmin());

// some activities and assertions...

// At this point, we have deleted the admin, so return null.
when(userRepo.findOne(1)).thenReturn(null);

// more activities and assertions...
或者,如果您正在尝试完全集成测试您的应用程序,并且您正在使用JPA,那么定义一个由HSQLDB或H2等实现的嵌入式内存数据库相对简单。在这里,您需要在测试上下文中定义单独的数据源和实体管理器,并配置Hibernate(在测试上下文中)以生成不存在的模式实体。通过这种方式,您可以测试将实体插入数据库,以及它们的ID是否正确生成、查询、更新和删除。所有这些都在您的构建过程中

我最近开发了一个演示应用程序,可以在GitHub中找到:


。。。虽然它演示了多个数据源的使用,但可能还有更简单的教程。

我可以通过这样做来解决这个问题:

MvcResult result=this.mockMvc.perform(…).andReturn()

andReturn()调用用于将值返回到结果变量中

字符串位置=result.getResponse().getHeader(“位置”)

现在我可以使用以下场景进行测试:POST(创建)、GET(找到)、DELETE(确定)、GET(未找到)

以下是整个测试:

HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + PASSWORD);

MvcResult resultPost = this.mockMvc.perform(
    post("/admin").headers(httpHeaders)
    .contentType(MediaType.APPLICATION_JSON)
    .accept(MediaType.APPLICATION_JSON)
    .content("{ \"firstname\" : \"" + admin0.getFirstname() + "\", \"lastname\" : \"" + admin0.getLastname() + "\", \"email\" : \"" + admin0.getEmail() + "\", \"login\" : \"" + admin0.getLogin() + "\", \"password\" : \"" + admin0.getPassword() + "\", \"passwordSalt\" : \"" + admin0.getPasswordSalt() + "\" }")
).andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.firstname").value(admin0.getFirstname()))
.andExpect(jsonPath("$.lastname").value(admin0.getLastname()))
.andExpect(jsonPath("$.email").value(admin0.getEmail()))
.andExpect(jsonPath("$.login").value(admin0.getLogin()))
.andExpect(jsonPath("$.password").value(admin0.getPassword()))
.andExpect(jsonPath("$.passwordSalt").value(admin0.getPasswordSalt()))
.andExpect(header().string("Location", Matchers.containsString("/admin/")))
.andReturn();

String location = resultPost.getResponse().getHeader("Location");
Pattern pattern = Pattern.compile("(\\d+)$");
Matcher matcher = pattern.matcher(location);
matcher.find();
Long id = Long.parseLong(matcher.group(), 10);

MvcResult resultGet = this.mockMvc.perform(
        get("/admin/" + id)
        .headers(httpHeaders)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON))
        .andDo(print())
        .andExpect(status().isFound())
        .andReturn();
String responseContent = resultGet.getResponse().getContentAsString();

this.mockMvc.perform(
        delete("/admin/" + id)
        .headers(httpHeaders)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON))
        .andDo(print())
        .andExpect(status().isOk());

this.mockMvc.perform(
        get("/admin/" + id)
        .headers(httpHeaders)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON))
        .andDo(print())
        .andExpect(status().isNotFound());

我已经创建了一个deletehandler,以便在测试后立即从数据库中删除该项

在测试中使用deleteHandler:

this.mockMvc.perform(post(...)).andDo(deleteResult(repository));
并在
/src/test/java
结构中的某个位置创建以下类:

public class DeleteResultHandler implements ResultHandler {

    private CrudRepository<IDatabaseObject, String> repository;

    public DeleteResultHandler(CrudRepository repository) {
        this.repository = repository;
    }

    public static DeleteResultHandler deleteResult(CrudRepository repository) {
        return new DeleteResultHandler(repository);
    }

    @Override
    public void handle(MvcResult result) throws Exception {
        String location = result.getResponse().getHeader("Location");
        String id = location.substring(location.lastIndexOf("/") + 1);
        Optional<IDatabaseObject> o = repository.findById(id);
        if (!o.isPresent())
            return;
        repository.delete(o.get());
        // repository.deleteById(id);
    }

}
公共类DeleteResultHandler实现ResultHandler{
私有积存库;
公共DeleteResultHandler(CrudRepository存储库){
this.repository=存储库;
}
公共静态DeleteResultHandler deleteResult(CrudRepository存储库){
返回新的DeleteResultHandler(存储库);
}
@凌驾
公共无效句柄(MvcResult结果)引发异常{
字符串位置=result.getResponse().getHeader(“位置”);
字符串id=location.substring(location.lastIndexOf(“/”)+1);
可选o=repository.findById(id);
如果(!o.isPresent())
返回;
delete(o.get());
//repository.deleteById(id);
}
}

一切皆有可能。。。取决于你的回报。通常,当返回201(已创建)时,用于检索资源的链接应位于位置标头中。但如前所述,这取决于您如何实现yuor controller/service。@M.Deinum是的,我的位置标题是这样的,我可以看到它。重定向的URL也像我认为我应该尝试做的是提取该URL并在GET和DELETE请求中使用它。然后你可以使用该位置头并执行GET和/或DELETE请求。你基本上是这样模拟你的客户机的。谢谢,我看了你的教程,它似乎更侧重于测试的配置。我正在尝试从响应头中提取URL。mockMvc使在控制台日志中打印整个响应变得很容易,但我想知道如何提取该URL,以便在我的测试中进一步使用它。