2011-05-25 20 views
8

我正在寫一些代碼,對當前時間進行日期和時間計算。在喬達時代,這是通過一個(Java)構造函數來訪問的,因爲它是一個不可變的對象。我需要能夠模擬,以便new DateTime()返回一個特定的常量瞬間,所以我可以做明智的測試斷言,但保留所有其他DateTime方法。在Grails中,有沒有一種使用喬達時間模擬當前時間的好方法?

這證明是令人討厭的。 Grails mockFor(DateTime, true)不會讓我模擬一個Java構造函數,但是沒有明顯的或可讀的非構造函數在Joda中獲取時間。

唯一可用的選項似乎涉及像JMockit或EasyMock 3類嘲諷這樣的低級JVM技術,這是Grails的一個難題。有沒有簡單/直接的方式來實現這一目標?

回答

5

我們結束了與now()方法創建dateService只有當使用MockTimeService。在單元測試中,我們使用

domainInstance.dateService = [ now: { currentTime } ] as DateService 

其中currentTime是一個單元測試類字段嘲笑它。這強加了每個人對dateService(我們唯一的近乎全球的依賴)的依賴,並且對於src類必須手動傳遞它。

單元測試,OTOH,看起來很清楚。

+0

很棒的回答。我曾經希望避免爲了讓這個測試成爲可能而設計不同的系統架構,但我學到了很多關於模擬構造函數的痛苦。 – 2011-05-26 13:06:50

+0

謝謝。我嘗試避免直接的構造函數調用 - 它違反了「接口代碼」,創建的實例不能像測試中那樣外化,有時會導致「構造Blob」。所以它歸結爲一種依賴注入的形式。 – 2011-05-26 13:54:42

2

你可以只使用老式的OO原則,例如

interface TimeService { 
    DateTime getCurrentTime() 

    // other time-related methods 
    } 

    class JodaTimeService implements TimeService { 
    DateTime getCurrentTime() { 
     new DateTime() 
    } 
    } 

    class MockTimeService implements TimeService { 
    DateTime getCurrentTime() { 
     // return a fixed point in time 
    } 
    } 

您的代碼應該通過依賴注入來獲得對TimeService實現的引用。在resources.groovy運行測試

import grails.util.Environment 

beans = {  
    if (Environment.current == Environment.TEST) { 
     timeService(MockTimeService) 

    } else { 
     timeService(JodaTimeService) 
    } 
} 
+0

獨立地與上面相同的方法,非常感謝,並深入解決了一個很好的解決方案。另外感謝使用測試環境切換的提示。 – 2011-05-26 13:05:13

18

我知道這已被接受,但隨着喬達時間,你可以凍結並將其設置爲任何你喜歡的。所以你可以凍結時間,提前時間,及時倒退。如果你一直在使用Joda,你的物品會隨着你設定的時間而「現在」。

// Stop time (and set a particular point in time): 
DateTimeUtils.setCurrentMillisFixed(whenever); 

// Advance time by the offset: 
DateTimeUtils.setCurrentMillisOffset(offsetFromCurrent); 

// Restore time (you could do this in an @After method) 
DateTimeUtils.setCurrentMillisSystem(); 
+0

Oooh整潔,我一直希望有這樣的事情。這將使測試更容易。偉大的提示! – 2011-05-26 14:08:29

+0

現在我感到很蠢,因爲我們在喬達。這是一個更好的方法。好,我學到了一些東西。 – 2011-05-29 08:11:39