用Mockito模拟局部作用域对象的方法

我需要一些帮助:

例:

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

我想在我的testing中模拟obj1.method1() ,但要透明,所以我不想做代码的更改和更改。 有没有办法在Mockito做到这一点?

如果你真的想要避免触摸这个代码,你可以使用Powermockito (PowerMock for Mockito)。

有了这个,在其他许多事情中,你可以用一个非常简单的方法来模拟新物体的构造 。

@edutesoy的答案指向了PowerMockito的文档,并提到构造函数嘲讽作为一个提示,但没有提到如何将这个问题应用到当前问题中。

这是一个基于此的解决scheme。 从这个问题的代码:

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

下面的testing将通过准备PowerMock实例化它的类(在这个例子中我称之为MyClass)并让PowerMockito存根MyObject类的构造函数来创buildMyObject实例类的模拟,然后让你存根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 } } 

用你的内部method1()调用将返回你想要的。

如果你喜欢单行程序,你可以通过内联创build模拟和存根来缩短代码:

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

没门。 你需要一些dependency injection,而不是让obj1实例化它应该由一些工厂提供。

 MyObjectFactory factory; public void setMyObjectFactory(MyObjectFactory factory) { this.factory = factory; } void method1() { MyObject obj1 = factory.get(); obj1.method(); } 

那么你的testing看起来像:

 @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中创build一个工厂方法来做到这一点:

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

然后用PowerMock来模拟。

但是,通过嘲讽本地作用域对象的方法,您将依赖于方法的实现部分保持不变。 所以你失去了重构该方法的一部分,而不会中断testing的能力。 另外,如果你在模拟中存储了返回值,那么你的unit testing可能会通过,但是当使用真实对象的时候,这个方法可能会出现意外的行为。

总之,你可能不应该这样做。 相反,让testing驱动你的代码(又名TDD),你会得到一个解决scheme,如:

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

通过依赖关系,你可以轻松地模拟unit testing。

最好的方法是不要触摸代码,并模拟构造函数,就像在这个例子中模拟一个方法内的File对象的创build一样。 不要忘记将在@PrepareForTest中创build文件的类。

 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); } }