2011-06-29 36 views
22

對象,我需要一些幫助,這:懲戒的局部範圍與方法的Mockito

例子:

void method1{ 
    MyObject obj1=new MyObject(); 
    obj1.method1(); 
} 

我想嘲笑obj1.method1()在我的測試,但是是透明的,所以我不想讓和更改代碼。 有沒有什麼辦法可以在Mockito中做到這一點?

回答

13

如果你真的想避免觸摸這段代碼,你可以使用Powermockito(PowerMock for Mockito)。

有了這個,在許多其他事情,你可以在一個非常簡單的方式mock the construction of new objects

+0

注意到了,但爲了將來的使用我會用PowerMock – Xoke

+0

@edutesoy你能寫完整的代碼嗎?我無法理解你的想法。我沒有看到構造函數模擬和局部變異模擬之間的關係。 – gstackoverflow

+1

如果我使用EclEmma進行代碼覆蓋,此方法的問題在於,如果將MyClass.class添加到@PrepareForTest而不是實際的代碼覆蓋範圍內,EclEmma會爲MyClass提供0%的代碼覆蓋率。我認爲這是EclEmma工具的限制或缺陷。任何想法來解決這個問題? –

9

沒辦法。你將需要一些依賴注入,即不是讓obj1實例化它應該由某個工廠提供。

MyObjectFactory factory; 

public void setMyObjectFactory(MyObjectFactory factory) 
{ 
    this.factory = factory; 
} 

void method1() 
{ 
    MyObject obj1 = factory.get(); 
    obj1.method(); 
} 

然後你的測試看起來像:

@Test 
public void testMethod1() throws Exception 
{ 
    MyObjectFactory factory = Mockito.mock(MyObjectFactory.class); 
    MyObject obj1 = Mockito.mock(MyObject.class); 
    Mockito.when(factory.get()).thenReturn(obj1); 

    // mock the method() 
    Mockito.when(obj1.method()).thenReturn(Boolean.FALSE); 

    SomeObject someObject = new SomeObject(); 
    someObject.setMyObjectFactory(factory); 
    someObject.method1(); 

    // do some assertions 
} 
+2

這是我的想法,但因爲我需要這增加了更多不必要的代碼從本地作用域對象中嘲笑一種方法。 – Xoke

+0

哪部分代碼是不必要的? –

+2

我不想創建Factory來創建myObject的新實例,並且代碼是不必要的,因爲它僅用於模擬局部作用域變量的方法。 – Xoke

1

您可以通過在爲MyObject創建一個工廠方法做到這一點:

class MyObject { 
    public static MyObject create() { 
     return new MyObject(); 
    } 
} 

然後嘲笑與PowerMock

但是,通過嘲笑本地作用域對象的方法,您將依賴於該方法實現的那部分保持不變。所以你失去了重構該方法的那部分而不破壞測試的能力。此外,如果您在模擬中存儲了返回值,那麼您的單元測試可能會通過,但是在使用真實對象時該方法可能會出現意外行爲。

總之,你應該不要試圖做到這一點。相反,讓試駕你的代碼(又名TDD),你會得出一個解決方案,如:

void method1(MyObject obj1) { 
    obj1.method1(); 
} 

傳遞的依賴,你可以很容易地嘲笑了單元測試。

1

最好的方法是不要觸摸代碼並模擬構造函數,就像在這個例子中嘲笑方法內部的File對象的創建一樣。 不要忘記將要在@PrepareForTest中創建文件的類。

package hello.easymock.constructor; 

import java.io.File; 

import org.easymock.EasyMock; 
import org.junit.Assert; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.powermock.api.easymock.PowerMock; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

@RunWith(PowerMockRunner.class) 
@PrepareForTest({File.class}) 
public class ConstructorExampleTest { 

    @Test 
    public void testMockFile() throws Exception { 

     // first, create a mock for File 
     final File fileMock = EasyMock.createMock(File.class); 
     EasyMock.expect(fileMock.getAbsolutePath()).andReturn("/my/fake/file/path"); 
     EasyMock.replay(fileMock); 

     // then return the mocked object if the constructor is invoked 
     Class<?>[] parameterTypes = new Class[] { String.class }; 
     PowerMock.expectNew(File.class, parameterTypes , EasyMock.isA(String.class)).andReturn(fileMock); 
     PowerMock.replay(File.class); 

     // try constructing a real File and check if the mock kicked in 
     final String mockedFilePath = new File("/real/path/for/file").getAbsolutePath(); 
     Assert.assertEquals("/my/fake/file/path", mockedFilePath); 
    } 

} 
24

從@edutesoy點PowerMockito的文檔的答案,並提到模擬構造函數作爲提示,但沒有提到如何適用於當前問題的問題。

以下是基於此的解決方案。以從問題的代碼:

public class MyClass { 
    void method1{ 
     MyObject obj1=new MyObject(); 
     obj1.method1(); 
    } 
} 

下面的測試將通過準備實例化它的類創建的MyObject實例類的模擬(在這個例子中,我叫它MyClass的)與PowerMock,讓PowerMockito存根爲MyObject類的構造函數,然後讓你存根MyObject的實例方法1()調用:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(MyClass.class) 
public class MyClassTest { 
    @Test 
    public void testMethod1() {  
     MyObject myObjectMock = mock(MyObject.class); 
     when(myObjectMock.method1()).thenReturn(<whatever you want to return>); 
     PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(myObjectMock); 

     MyClass objectTested = new MyClass(); 
     objectTested.method1(); 

     ... // your assertions or verification here 
    } 
} 

隨着您的內部方法1()調用將返回你想要什麼。

如果你喜歡的單行可以使代碼更短,通過創建模擬和存根聯:

MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock(); 
+1

如果我使用EclEmma進行代碼覆蓋,此方法的問題在於,如果我將MyClass.class添加到@PrepareForTest而不是實際的代碼覆蓋範圍內,EclEmma會爲MyClass提供0%的代碼覆蓋率。我認爲這是EclEmma工具的限制或缺陷。任何想法來解決這個問題? –

+1

@Vidyasagar我在Sonar/Jacoco中發現了同樣的問題。 –