對象,我需要一些幫助,這:懲戒的局部範圍與方法的Mockito
例子:
void method1{
MyObject obj1=new MyObject();
obj1.method1();
}
我想嘲笑obj1.method1()
在我的測試,但是是透明的,所以我不想讓和更改代碼。 有沒有什麼辦法可以在Mockito中做到這一點?
對象,我需要一些幫助,這:懲戒的局部範圍與方法的Mockito
例子:
void method1{
MyObject obj1=new MyObject();
obj1.method1();
}
我想嘲笑obj1.method1()
在我的測試,但是是透明的,所以我不想讓和更改代碼。 有沒有什麼辦法可以在Mockito中做到這一點?
如果你真的想避免觸摸這段代碼,你可以使用Powermockito(PowerMock for Mockito)。
有了這個,在許多其他事情,你可以在一個非常簡單的方式mock the construction of new objects。
沒辦法。你將需要一些依賴注入,即不是讓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
}
您可以通過在爲MyObject創建一個工廠方法做到這一點:
class MyObject {
public static MyObject create() {
return new MyObject();
}
}
然後嘲笑與PowerMock。
但是,通過嘲笑本地作用域對象的方法,您將依賴於該方法實現的那部分保持不變。所以你失去了重構該方法的那部分而不破壞測試的能力。此外,如果您在模擬中存儲了返回值,那麼您的單元測試可能會通過,但是在使用真實對象時該方法可能會出現意外行爲。
總之,你應該不要試圖做到這一點。相反,讓試駕你的代碼(又名TDD),你會得出一個解決方案,如:
void method1(MyObject obj1) {
obj1.method1();
}
傳遞的依賴,你可以很容易地嘲笑了單元測試。
最好的方法是不要觸摸代碼並模擬構造函數,就像在這個例子中嘲笑方法內部的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);
}
}
從@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();
如果我使用EclEmma進行代碼覆蓋,此方法的問題在於,如果我將MyClass.class添加到@PrepareForTest而不是實際的代碼覆蓋範圍內,EclEmma會爲MyClass提供0%的代碼覆蓋率。我認爲這是EclEmma工具的限制或缺陷。任何想法來解決這個問題? –
@Vidyasagar我在Sonar/Jacoco中發現了同樣的問題。 –
注意到了,但爲了將來的使用我會用PowerMock – Xoke
@edutesoy你能寫完整的代碼嗎?我無法理解你的想法。我沒有看到構造函數模擬和局部變異模擬之間的關係。 – gstackoverflow
如果我使用EclEmma進行代碼覆蓋,此方法的問題在於,如果將MyClass.class添加到@PrepareForTest而不是實際的代碼覆蓋範圍內,EclEmma會爲MyClass提供0%的代碼覆蓋率。我認爲這是EclEmma工具的限制或缺陷。任何想法來解決這個問題? –