如何对使用Repository的Service层进行Spring Unit Test?

时间:2017-08-07 17:36:38

标签: java spring unit-testing

我正在开发一个Spring REST应用程序,并且有一个服务来访问数据库(使用存储库)并恢复N个最后文档(I&#39 ; m使用MongoDB)。

所以,我的服务有这个功能:

@Async
public CompletableFuture<List<MessageLogViewDto>> listLogs(int total) {
  // Request the entities using PageRequest to list only last N registers ordered by DATE (DESC). I don't known a better way to do this!
  PageRequest page = new PageRequest(0, total, Sort.Direction.DESC, "Date");
  Page<MessageLog> messages = logRepo.findAll(page);

  // Convert the Entity to DTO
  List<MessageLogViewDto> messageList = messages.getContent().stream().map(
    message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());

  return CompletableFuture.completedFuture(messageList);
}

(convertDto是一个将实体转换为Dto的类)。

好的,如何对此进行单元测试? 我在几个博客上看到单元测试不应该连接到数据库。 如果我使用真正的测试数据库,这个单元测试可以变得非常简单,但我想知道在没有数据库的情况下进行此测试的更好方法(如果可能的话)。

我可以模拟存储库和函数findAll,但如果我模拟findAll方法,我也必须模拟返回值。我不知道如何模拟Page<Object>响应,因为Page是一个接口,我将不得不在模拟中实现所有方法。

对于我的测试,我正在考虑使用不同的日期创建3个随机message实体,并检查是否只返回了最后一条消息。

类似的东西:

@Test
  public void shouldReturnLastMessage() {
    int total = 1;

    // mock findAll
    // mock first entity 
    // mock a second entity
    // mock a third entity
    // ??? = must return a Page<MessageLog> with 3 entitys, how?
    when(dbLogRepoMock.findAll(any(PageRequest.class))).thenReturn(???);

    // call the service
    List<MessageLogViewDto> result = logService.listLogs(total).join();

    // check returned value
    assertThat(result.size()).isEqualTo(total);
    assertThat(result.get(0).getMensagem()).isEqualTo("third message"); // last message

    // check if repository was called
    verify(dbLogRepoMock).findAll(any(PageRequest.class));
}

任何帮助?

1 个答案:

答案 0 :(得分:1)

这可能是一个有点引起争议的观点,但我会专注于单元测试您的转换器,因为此方法中没有其他有意义的自定义逻辑。

看看它的方法:

// 1 - Create an instance of a Spring class
PageRequest page = new PageRequest(0, total, Sort.Direction.DESC, "Date");
// 2 - Query a Spring Repository with the Spring Class
Page<MessageLog> messages = logRepo.findAll(page);

// 3 - CUSTOM LOGIC to convert result to custom DTO class
List<MessageLogViewDto> messageList = messages.getContent().stream().map(
        message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());

// 4 - Return a Java standard CompleteableFuture 
return CompletableFuture.completedFuture(messageList);

即使您要为此方法编写单元测试,

1是简单的类实例化 2使用1的结果 - 但你无论如何都要创建一个模拟响应,所以它不会有意义地运用1。 3是您的转换器逻辑,您可以直接编写独立测试 4正在利用核心java功能,你不太可能发现

的错误

此外,请查看方法的签名:

public CompletableFuture<List<MessageLogViewDto>> listLogs(int total) {

它接受一个整数total并返回CompleteableFuture(我们不关心测试的实现,因为它不是此方法的核心功能/是内置的),包含一个元素列表。假设是一个大小为total的列表。

如果没有实际访问数据库,则必须将返回的数据模拟为预期大小。确定结果大小的实际逻辑是数据访问框架的一部分,因此您甚至无法验证输出数据大小是否与方法的输入参数实际匹配。在我的估计中没有多少价值。

我通常不专注于单元测试数据访问方法,这些方法只是在没有任何有意义的逻辑的情况下访问和返回数据。在这种情况下,只有有意义的逻辑出现在您的转换器中,因此我将专注于测试。

您可以提取类似的方法:

List<MessageLogViewDTO> convertMessageLogsToMessageLogViewDTO(List<MessageLog> messages) {
  return messages.getContent().stream().map(
    message -> convertDto.toMessageLogView(message)).collect(Collectors.toList());
}

然后你可以简单地用各种情况测试该方法(有效数据,空/空列表等等。