2017-09-12 32 views
-1

我想單元測試使用java.time.LocalDateTime的代碼。我能夠得到模擬工作,但是當我添加時間(無論是分鐘還是天)時,我最終得到的值爲nullLocalDateTime.of返回null

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ LocalDateTime.class }) 
public class LocalDateTimeMockTest 
{ 
    @Test 
    public void shouldCorrectlyCalculateTimeout() 
    { 
     // arrange 
     PowerMockito.mockStatic(LocalDateTime.class); 
     LocalDateTime fixedPointInTime = LocalDateTime.of(2017, 9, 11, 21, 28, 47); 
     BDDMockito.given(LocalDateTime.now()).willReturn(fixedPointInTime); 

     // act 
     LocalDateTime fixedTomorrow = LocalDateTime.now().plusDays(1); //shouldn't this have a NPE? 

     // assert 
     Assert.assertTrue(LocalDateTime.now() == fixedPointInTime); //Edit - both are Null 
     Assert.assertNotNull(fixedTomorrow); //Test fails here 
     Assert.assertEquals(12, fixedTomorrow.getDayOfMonth()); 
    } 
} 

我明白(嗯,我想我這樣做)是LocalDateTime是不可改變的,而且我認爲我應該得到一個新的實例,而不是null值。

原來,這是.of方法,它給了我一個null值。爲什麼?

+0

您是否嘗試運行代碼沒有嘲笑? powermockito很有可能造成這個問題。 – Ali

+1

並考慮重新考慮使用PowerMock(ito);-) – GhostCat

+0

當嘲笑一個Java「系統」類(如'java.time.LocalDateTime')時,PowerMock要求非系統類*使用*該類在'@ PrepareForTest'中列出(正如其他答案中已經指出的那樣)。但根據我的經驗,像這樣的測試中最大的錯誤是甚至會引起嘲笑日期/時間戳或系統時鐘的想法。如果SUT使用'LocalDateTime.now()',那麼它很可能應該允許從外部(通過調用者代碼)給出一個日期對象,這將使得編寫測試時沒有任何嘲諷的微不足道。去過也做過。 –

回答

4

按照documentation

使用PowerMock.mockStatic(ClassThatContainsStaticMethod.class)嘲笑此類的所有方法。

和:

請注意,您可以模擬一個類的靜態方法,即使類是final。該方法也可以是最終的。 若要僅模擬特定類的靜態方法,請參閱文檔中的partial mocking部分

要嘲笑系統類中的靜態方法,您需要遵循this的方法。

你告訴它模擬所有的靜態方法,但沒有提供of()方法的模擬。

解決方案:添加模擬爲of()方法,或改變使用局部嘲笑,所以of()方法沒有嘲笑。

基本上,閱讀並遵循文檔的說明。

1

所以PowerMockito.mockStatic搞亂了下一行代碼。只需移動fixedPointInTime的實例即可在mockStatic ....之前執行,使其一切正常。

4

@Andreas' answer正確地解釋了PowerMock的用法(並且你也計算出了in your own answer)。

我只是想添加一個不同的方法。爲了測試當前日期/時間,您可以使用java.time.Clock。通過此課程,您可以創建fixed clock(始終返回相同的當前日期/時間的Clock)和use it in your test。因此,不需要模擬靜態方法(我不得不從測試類中刪除PowerMock註釋)。唯一的區別是,時鐘必須傳遞給now()方法:

@Test 
public void shouldCorrectlyCalculateTimeout() { 
    // create a clock that always returns the same current date/time 
    LocalDateTime fixedPointInTime = LocalDateTime.of(2017, 9, 11, 21, 28, 47); 
    ZoneId zone = ZoneId.systemDefault(); 
    Clock clock = Clock.fixed(fixedPointInTime.atZone(zone).toInstant(), zone); 

    // use the clock in now() method 
    LocalDateTime fixedTomorrow = LocalDateTime.now(clock).plusDays(1); 

    // assert (use equals() instead of == because it doesn't return the same instance) 
    Assert.assertTrue(LocalDateTime.now(clock).equals(fixedPointInTime)); 
    Assert.assertNotNull(fixedTomorrow); 
    Assert.assertEquals(12, fixedTomorrow.getDayOfMonth()); 
} 

我不得不使用equals()方法(而不是==)比較的日期,因爲now(clock)創建一個新的實例,儘管所有實例將對應到相同的日期/時間(這是重要的,IMO)。


PS:在上述我使用的JVM默認時區(ZoneId.systemDefault())的代碼。唯一的問題是它can be changed without notice, even at runtime,所以最好總是明確你正在使用哪一個。

在這個特定的代碼中,時區部分被忽略,因此您可以使用任何區域,這並沒有太大的區別 - 除非您在夏令時切換時發生時區和本地日期的組合,這會產生意想不到的結果。

如果您不想依賴於此,則可以替換ZoneId.systemDefault()並改爲使用ZoneOffset.UTC(UTC不具有任何夏令時效果)。在這種情況下,你可以創建你的時鐘是這樣的:

ZoneOffset utc = ZoneOffset.UTC; 
Clock clock = Clock.fixed(fixedPointInTime.toInstant(utc), utc); 
0

在你的類像

public class SomeClass{ 

    public static void main(String[] args) { 
     LocalDateTime now = getCurrentLocalDateTime(); 
     System.out.println(now); 
    } 

    private LocalDateTime getCurrentLocalDateTime() { 
     return LocalDateTime.now(); 
    } 

} 

創建的方法和在Test類使用:

@PrepareForTest(SomeClass.class) 

@RunWith(PowerMockRunner.class) 

在測試用例:

LocalDateTime tommorow= LocalDateTime.now().plusDays(1); 

SomeClass classUnderTest = PowerMockito.spy(new SomeClass()); 

PowerMockito.when(classUnderTest, "getCurrentLocalDateTime").thenReturn(tommorow);