2017-03-09 55 views
0

我有問題斷言由LocalDateTime.now()方法產生的時間,我想斷言整個查詢,而忽略它產生的時間。 下面是代碼:如何斷言查詢,同時忽略由LocalDateTime.now()產生的時間差異?

@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); 

這裏是比較失敗的細節,這樣你就可以有更多的想法是什麼,我需要忽略:Comparison Failure Window Screenshot.

這裏是我的測試方法:

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); 
} 

我試圖做匹配()從Mockito,沒有運氣。我似乎無法得到正則表達式的工作。喜歡匹配一切,但以下正則表達式:(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3})。選擇我需要忽略的部分。

+0

最好不要對我正在測試的類/方法做任何更改。 – Amiko

回答

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

+0

感謝您以更詳細的方式解釋。我從你的回答中學到了很多東西。它的工作原理。我有這個問題,有沒有辦法用Mockito中的匹配方法來做到這一點,而不需要觸摸/爲我正在測試的類添加任何額外的東西?只是好奇,如果有匹配的方式()。像忽略除DateTime之外的所有內容。 – Amiko

+0

@Amiko我想不出一個用'matches'來完成它的好方法,而不會將你期望的查詢變成一個龐大且不可讀的正則表達式本身(儘管你可以使用轉義工具構建查詢表達式,例如,並且在你寫的表達)。這仍然很難閱讀和維護,並且不會測試正確的日期是您查詢的一部分。另一個解決方案是使用ArgumentCaptor捕獲查詢並解析或去除日期,具有類似的可讀性/可維護性/覆蓋成本。無論如何我都不能推薦任何解決方案。 –

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),單元測試第二種方法是微不足道的。