2012-08-09 74 views
26

我有一個函數使用當前時間進行一些計算。我想用mockito來嘲笑它。如何在Java中使用Mockito模擬新日期()

類的一個例子,我想測試:

public class ClassToTest { 
    public long getDoubleTime(){ 
     return new Date().getTime()*2; 
    } 
} 

我想是這樣的:

@Test 
public void testDoubleTime(){ 
    mockDateSomeHow(Date.class).when(getTime()).return(30); 
    assertEquals(60,new ClassToTest().getDoubleTime()); 
} 

是否有可能來嘲笑嗎?我不想更改「測試」代碼以便測試。

+3

你爲什麼不改變測試代碼?更可測試的代碼通常更加鬆散耦合...爲什麼你不想要那個? – blank 2012-08-09 16:27:50

+0

[Override Java System.currentTimeMillis]的可能重複(http://stackoverflow.com/questions/2001671/override-java-system-currenttimemillis) – 2012-08-09 16:30:43

+0

...另一件事 - 更改'測試'的代碼很容易 - 你'當你犯了一個錯誤時,已經有測試告訴你 - 改變未經測試的代碼...另一方面,你需要邁克爾羽毛kung foo;) – blank 2012-08-09 16:46:40

回答

39

正確的做法是重構代碼,使其更具可測試性,如下所示。 重組你的代碼,以消除日期的直接依賴關係可以讓你注入的正常運行時間和測試運行時不同的實現:

interface DateTime { 
    Date getDate(); 
} 

class DateTimeImpl implements DateTime { 
    @Override 
    public Date getDate() { 
     return new Date(); 
    } 
} 

class MyClass { 

    private final DateTime dateTime; 
    // inject your Mock DateTime when testing other wise inject DateTimeImpl 

    public MyClass(final DateTime dateTime) { 
     this.dateTime = dateTime; 
    } 

    public long getDoubleTime(){ 
     return dateTime.getDate().getTime()*2; 
    } 
} 

public class MyClassTest { 
    private MyClass myClassTest; 

    @Before 
    public void setUp() { 
     final Date date = Mockito.mock(Date.class); 
     Mockito.when(date.getTime()).thenReturn(30L); 

     final DateTime dt = Mockito.mock(DateTime.class); 
     Mockito.when(dt.getDate()).thenReturn(date); 

     myClassTest = new MyClass(dt); 
    } 

    @Test 
    public void someTest() { 
     final long doubleTime = myClassTest.getDoubleTime(); 
     assertEquals(60, doubleTime); 
    } 
} 
+1

我同意。我一直這樣做。很好的工作,對原始代碼的改變是最小的,測試很簡單。 – stmax 2012-08-09 16:37:44

+12

這是解決這個問題的經典方法(你稱之爲'DateTime'可能更具描述性地稱爲'Clock'或類似的東西)。但是,這確實意味着重構您的代碼並純粹爲了測試而增加一點複雜性,這有點代碼味道。 – 2012-08-09 20:09:31

+0

所以我做了,我認爲這是一個很好的方法,但問題是如何與Mockito做到這一點:D – 2012-08-09 21:07:02

6

可以使用PowerMock,其增強的Mockito能夠嘲笑做到這一點靜態方法。那麼你可以mock System.currentTimeMillis(),這是最後從new Date()得到的時間。

可能。我不會就你是否應該提出意見。

+1

我很想知道如何做這樣的事情,這是問題的目的。你有例子嗎? – 2012-08-09 21:07:46

+0

這是來自[Powermock文檔](http://code.google.com/p/powermock/wiki/MockSystem)。應該與PowerMockito一樣工作 – Brad 2012-08-09 22:13:00

+1

盲目地將每個「新」變成一個包裝對象,並通過注入的依賴引入一個間接尋址,這隻會讓代碼變得非常冗長和困難。如果你在你的類中創建了一個ArrayList或一個HashMap,你現在可以創建一個ArrayListFactory或一個HashMapFactory並將它注入到你的類中?在所有使用「新」的地方盲目地使用PowerMock可能會創建一個非常緊密耦合的系統。能夠分辨PowerMock這樣的工具是否合適是成爲熟練開發人員的一部分 – Aneesh 2014-09-04 14:58:06

14

如果你有遺留代碼,你不能重構,你不希望影響System.currentTimeMillis(),試試這個使用PowermockPowerMockito

//note the static import 
import static org.powermock.api.mockito.PowerMockito.whenNew; 

@PrepareForTest({ LegacyClassA.class, LegacyClassB.class }) 

@Before 
public void setUp() throws Exception { 

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
    sdf.setTimeZone(TimeZone.getTimeZone("PST")); 

    Date NOW = sdf.parse("2015-05-23 00:00:00"); 

    // everytime we call new Date() inside a method of any class 
    // declared in @PrepareForTest we will get the NOW instance 
    whenNew(Date.class).withNoArguments().thenReturn(NOW); 

} 

public class LegacyClassA { 
    public Date getSomeDate() { 
    return new Date(); //returns NOW 
    } 
} 
1

的一種方法,這並沒有直接回答這個問題,但可能解決根本問題(具有可重複性測試),允許Date作爲測試參數並將委託添加到默認日期。

像這樣

public class ClassToTest { 

    public long getDoubleTime() { 
     return getDoubleTime(new Date()); 
    } 

    long getDoubleTime(Date date) { // package visibility for tests 
     return date.getTime() * 2; 
    } 
} 

在生產代碼,您使用getDoubleTime()和測試針對getDoubleTime(Date date)

相關問題