用Mockito模拟局部范围对象的方法

Xok*_*oke 42 mocking local object mockito

我需要一些帮助:

例:

void method1{
    MyObject obj1=new MyObject();
    obj1.method1();
}
Run Code Online (Sandbox Code Playgroud)

我想obj1.method1()在我的测试中模拟,但要透明,所以我不想制作和更改代码.在Mockito有什么办法吗?

ras*_*orp 44

@edutesoy的答案指向PowerMockito的文档,并提到构造函数模拟作为提示,但没有提到如何将其应用于问题中的当前问题.

这是基于此的解决方案.从问题中获取代码:

public class MyClass {
    void method1{
        MyObject obj1=new MyObject();
        obj1.method1();
    }
}
Run Code Online (Sandbox Code Playgroud)

下面的测试将通过准备使用PowerMock实例化它的类(在本例中我称之为MyClass)并让PowerMockito存根MyObject类的构造函数,然后让你存根MyObject实例method1来创建MyObject实例类的模拟. ()电话:

@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 
    }
}
Run Code Online (Sandbox Code Playgroud)

这样你的内部method1()调用将返回你想要的.

如果你喜欢单行,你可以通过创建mock和stub内联来缩短代码:

MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock();   
Run Code Online (Sandbox Code Playgroud)

  • 如果我使用 EclEmma 进行代码覆盖率,这种方法的问题是,如果我将 MyClass.class 添加到 @PrepareForTest 而不是实际的代码覆盖率,则 EclEmma 为 MyClass 提供 0% 的代码覆盖率。我认为这是 EclEmma 工具的限制或错误。有什么想法可以克服这个问题吗? (3认同)
  • @Vidyasagar 我在 Sonar/Jacoco 中发现了同样的问题。 (2认同)

Mr.*_*art 26

如果您真的想避免触及此代码,可以使用Powermockito(PowerMock for Mockito).

有了这个,在许多其他事情中,你可以用一种非常简单的方式模拟新对象的构造.

  • 如果我使用 EclEmma 进行代码覆盖,这种方法的问题在于,如果我将 MyClass.class 添加到 @PrepareForTest 而不是实际代码覆盖率,EclEmma 会为 MyClass 提供 0% 的代码覆盖率。我认为这是 EclEmma 工具的限制或错误。有什么想法可以克服这个问题吗? (2认同)

Bor*_*vić 13

没门.你需要一些依赖注入,即不是实例化obj1,它应该由一些工厂提供.

MyObjectFactory factory;

public void setMyObjectFactory(MyObjectFactory factory)
{
  this.factory = factory;
}

void method1()
{
  MyObject obj1 = factory.get();
  obj1.method();
}
Run Code Online (Sandbox Code Playgroud)

然后你的测试看起来像:

@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
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我的想法,但这是添加更多不必要的代码,因为我需要从本地范围对象模拟一个方法. (3认同)
  • 我不想创建工厂以创建myObject的新实例,并且该代码是不必要的,因为只需要模拟本地范围变量的方法. (2认同)

小智 12

在最新的mockito版本和junit5中,无需PowerMock就可以模拟新实例的创建和静态方法。

查看方法Mockito.mockConstruction()Mockito.mockStatic()

在你的情况下:

try (MockedConstruction<MyObject> myobjectMockedConstruction = Mockito.mockConstruction(MyObject.class,
        (mock, context) -> {
            given(mock.method1()).willReturn("some result"); //any additional mocking
        })) {
    underTest.method1();
    assertThat(myobjectMockedConstruction.constructed()).hasSize(1);
    MyObject mock = myobjectMockedConstruction.constructed().get(0);
    verify(mock).method1();
}
Run Code Online (Sandbox Code Playgroud)