如何在忽略LocalDateTime.now()产生的时间差的情况下断言查询?

时间:2017-03-09 13:42:27

标签: java unit-testing junit mocking mockito

我有问题断言由LocalDateTime.now()方法产生的时间,我想断言整个查询而忽略它产生的时间。 以下是代码:

jQuery 1.12.4

以下是比较失败的详细信息,因此您可以更加了解我需要忽略的内容:Comparison Failure Window Screenshot.

以下是我正在测试的方法:

 @Test
public void getLastDrawResult_lotteryIdPresent() {
    Optional<Long> lotteryId = Optional.of(3L);
    Optional<String> name = Optional.empty();
    Optional<String> byName = Optional.empty();
    Optional<String> byDate = Optional.empty();
    Optional<Boolean> jackpotOnly = Optional.empty();

    String query = QUERY_MAIN_BLOCK.replace("/*statement0*/", LocalDateTime.now().plusMinutes(buyingLock).toString()) + CONDITION_SEARCH_BY_LAST_DRAW_RESULT;
    String updatedQuery = query.replace("/*statement1*/", " WHERE d.lottery_info_id = 3") + " AND d.lottery_info_id = 3";

    DrawResultDto drawResultDto = DrawResultDto.builder().id(3L).build();
    List<DrawResultDto> expectedDrawResultDto = singletonList(drawResultDto);

    when(jdbcTemplate.query(updatedQuery, drawResultDtoQueryBuilder.queryMapper)).thenReturn(expectedDrawResultDto);
    List<DrawResultDto> actualDrawResultDto = drawResultDtoQueryBuilder.getLastDrawResult(lotteryId, name, byName, byDate, jackpotOnly);

    verify(jdbcTemplate).query(updatedQuery, drawResultDtoQueryBuilder.queryMapper);
    assertEquals(expectedDrawResultDto, actualDrawResultDto);

我试图从Mockito做匹配(),没有运气。我似乎无法让正则表达式工作。喜欢匹配一切但是下面的正则表达式:public List<DrawResultDto> getLastDrawResult(Optional<Long> lotteryId, Optional<String> name, Optional<String> byName, Optional<String> byDate, Optional<Boolean> jackpotOnly) { String query = QUERY_MAIN_BLOCK.replace("/*statement0*/", LocalDateTime.now().plusMinutes(buyingLock).toString()) + CONDITION_SEARCH_BY_LAST_DRAW_RESULT; StringBuilder builder = new StringBuilder(); if (lotteryId.isPresent()) { builder.append(query.replace("/*statement1*/", " WHERE d.lottery_info_id = " + lotteryId.get())) .append(String.format(" AND d.lottery_info_id = %d", lotteryId.get())); } else { builder.append(query); name.ifPresent(nameValue -> builder.append(" AND lower(li.name) LIKE '%").append(SqlUtils.escapeLike(nameValue).toLowerCase()).append("%' ")); jackpotOnly.ifPresent(jackpotOnlyValue -> { if (jackpotOnlyValue) { builder.append(" AND ").append(CONDITION_SEARCH_BY_JACKPOT_WIN); } }); byName.ifPresent(s -> builder.append(" ORDER BY li.name ").append(SqlUtils.escapeLike(s))); byDate.ifPresent(s -> builder.append(" ORDER BY nfd.nearestDrawDate ").append(SqlUtils.escapeLike(s))); if (!byName.isPresent() && !byDate.isPresent()){ builder.append(" ORDER BY li.name desc "); } } return jdbcTemplate.query(builder.toString(), queryMapper); } 。选择我需要忽略的部分。

2 个答案:

答案 0 :(得分:1)

好像你在代码之后编写测试:p(否则你就不会遇到这样的问题)。

我要做的是将localedatetime传递给函数,以便您可以对它进行单元测试:

public List<DrawResultDto> getLastDrawResult(LocalDateTime t, Optional<Long> lotteryId, Optional<String> name, Optional<String> byName, Optional<String> byDate, Optional<Boolean> jackpotOnly) {
    String query = QUERY_MAIN_BLOCK.replace("/*statement0*/", t.plusMinutes(buyingLock).toString()) +
    [...]
}

并保持与现有代码的一致性,保留实际签名:

public List<DrawResultDto> getLastDrawResult(Optional<Long> lotteryId, Optional<String> name, Optional<String> byName, Optional<String> byDate, Optional<Boolean> jackpotOnly) {
    return getLastDrawResult(LocaleDateTime.now(), Optional<Long> lotteryId, Optional<String> name, Optional<String> byName, Optional<String> byDate, Optional<Boolean> jackpotOnly);
}

现在你可以正确地测试第一种方法(通过传递一个在测试方法中构建的localedatetime),单元测试第二种方法是微不足道的。

答案 1 :(得分:1)

此处的最佳做法将涉及您注入Clock,这将允许您通过覆盖now方法访问所需的任何对象(包括LocalDateTime)。

public List<DrawResultDto> getLastDrawResult(
    Optional<Long> lotteryId,
    Optional<String> name,
    Optional<String> byName,
    Optional<String> byDate,
    Optional<Boolean> jackpotOnly,
    Clock clock) {
  LocalDateTime localDateTime = LocalDateTime.now(clock);
  /* ... */
}

Ash's answer中一样,您可以使用当前签名创建新SystemClock的方法重载,然后测试接受时钟的方法。

/** Your original method signature. No changes to any calling code. */
public List<DrawResultDto> getLastDrawResult(
    Optional<Long> lotteryId,
    Optional<String> name,
    Optional<String> byName,
    Optional<String> byDate,
    Optional<Boolean> jackpotOnly) {
  return getLastDrawResult(lotteryId, name, byName, byDate, jackpotOnly,
      Clock.systemDefaultZone());
}

/** Your original method implementation. Test this one. */
public List<DrawResultDto> getLastDrawResult(
    Optional<Long> lotteryId,
    Optional<String> name,
    Optional<String> byName,
    Optional<String> byDate,
    Optional<Boolean> jackpotOnly,
    Clock clock) {
  LocalDateTime localDateTime = LocalDateTime.now(clock);
  /* ... */
}

这样,在测试中,您可以传递Clock.fixed的值。

另外,您应该强烈考虑将此方法切换为参数化查询JdbcTemplate supports natively。你甚至可以support named parameters using related classes