【发布时间】:2011-09-25 02:27:22
【问题描述】:
我需要一些帮助:
例子:
void method1{
MyObject obj1=new MyObject();
obj1.method1();
}
我想在我的测试中模拟obj1.method1(),但要保持透明,所以我不想制作和更改代码。
有没有办法在 Mockito 中做到这一点?
【问题讨论】:
标签: object mocking local mockito
我需要一些帮助:
例子:
void method1{
MyObject obj1=new MyObject();
obj1.method1();
}
我想在我的测试中模拟obj1.method1(),但要保持透明,所以我不想制作和更改代码。
有没有办法在 Mockito 中做到这一点?
【问题讨论】:
标签: object mocking local mockito
@edutesoy 的答案指向PowerMockito 的文档,并提到构造函数模拟作为提示,但没有提到如何将其应用于问题中的当前问题。
这是一个基于此的解决方案。从问题中获取代码:
public class MyClass {
void method1 {
MyObject obj1 = new MyObject();
obj1.method1();
}
}
以下测试将创建一个模拟 MyObject 实例类,方法是使用 PowerMock 准备实例化它的类(在本例中我称之为 MyClass)并让 PowerMockito 存根MyObject 类,然后让你存根 MyObject 实例 method1() 调用:
@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
}
}
您的内部method1() 调用将返回您想要的。
如果您喜欢单行代码,您可以通过创建模拟和内联存根来缩短代码:
MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock();
【讨论】:
如果你真的想避免接触这段代码,你可以使用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
}
【讨论】:
method1 时拥有一个新的 MyObject 实例
您可以避免更改代码(尽管我推荐 Boris 的答案)并模拟构造函数,就像在此示例中模拟在方法内创建 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);
}
}
【讨论】:
您可以通过在 MyObject 中创建工厂方法来做到这一点:
class MyObject {
public static MyObject create() {
return new MyObject();
}
}
然后用 PowerMock 模拟它。
但是,通过模拟本地范围对象的方法,您将依赖于方法实现的那部分保持不变。因此,您失去了在不破坏测试的情况下重构该部分方法的能力。此外,如果你在 mock 中存根返回值,那么你的单元测试可能会通过,但在使用真实对象时,方法可能会出现意外行为。
总之,您可能不应该尝试这样做。相反,让测试驱动您的代码(又名 TDD),您会得到如下解决方案:
void method1(MyObject obj1) {
obj1.method1();
}
传入依赖项,您可以轻松地对其进行模拟以进行单元测试。
【讨论】:
如果您不喜欢使用 PowerMock,可以尝试以下方式:
public class Example{
...
void method1(){
MyObject obj1 = getMyObject();
obj1.doSomething();
}
protected MyObject getMyObject(){
return new MyObject();
}
...
}
这样写你的测试:
@Mock
MyObject mockMyObject;
@Test
void testMethod1(){
Example spyExample = spy(new Example());
when(spyExample.getMyObject()).thenReturn(mockMyObject);
//stub if required
doNothing().when(mockMyObject.doSomething());
verify(mockMyObject).doSomething();
}
【讨论】: