对象,我需要一些帮助,这:惩戒的局部范围与方法的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工具的限制或缺陷。任何想法来解决这个问题? –