2011-06-29 35 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中发现了同样的问题。 –