【问题标题】:How to mock instance inside constructor?如何在构造函数中模拟实例?
【发布时间】:2018-11-24 13:52:39
【问题描述】:

我有课

public class MockSampleUser {

    private Address address = null;

    public MockSampleUser(){
        address = new Address();
    }

    public String getAddress(){
        return address.getAddress();
    }

    class Address {
        public String getAddress(){
            return "Address";
        }
    }

}

单元测试:

import static org.junit.Assert.*;
import org.junit.Test;
import static org.mockito.Mockito.*;

public class MockSampleUserTest {

    @Test
    public void MockSampleUser(){

        MockSampleUser.Address address = mock(MockSampleUser.Address.class);
        MockSampleUser mockSampleUser = spy(MockSampleUser.class);

        when(address.getAddress()).thenReturn("New Address");
        String add = mockSampleUser.getAddress();
        assertEquals("New Address", add);

        add = mockSampleUser.getAddress();
        assertEquals("Address", add);
    }
}

我想写一个测试,当我调用 MockSampleUser 类的 getAddress 时它会返回“新地址”。 我想写一个测试,当我调用 MockSampleUser 类的 getAddress 时它会返回“新地址”。

【问题讨论】:

  • 上述实现有什么问题?你有什么错误吗?

标签: java junit mocking mockito junit5


【解决方案1】:

Mocking 旨在模拟被测对象的依赖关系,而不是模拟实现细节。
实际上address 不是客户端可以设置的依赖项,所以你没有办法用 Mockito 模拟它。
您应该做的是重载构造函数/更改实际构造函数以接受 Address 参数,或者如果依赖项是可变的,则为此字段提供设置器。
例如构造函数:

public MockSampleUser(){
    address = new Address();
}

public MockSampleUser(Address){
    this.address = address;
}

例如使用 setter :

public void setAddress(Address){
    this.address = address;
}

请注意,关于您的单元测试,您希望 getAddress() 在您多次调用它时返回不同的内容:

String add = mockSampleUser.getAddress();
assertEquals("New Address", add);

add = mockSampleUser.getAddress();
assertEquals("Address", add);

这是没有意义的,因为它引用了同一个对象,并且它的状态在两个断言之间没有改变。

如果你不允许地址依赖是可变的,你的测试应该看起来像(间谍在这里是无助的,它在大多数情况下是正确的):

@Test
public void MockSampleUser(){
    MockSampleUser.Address address = mock(MockSampleUser.Address.class);
    MockSampleUser mockSampleUser = new MockSampleUser(address);
    when(address.getAddress()).thenReturn("New Address");

    String add = mockSampleUser.getAddress();
    assertEquals("New Address", add);
    // or better, use the address object itself as expected
    assertEquals(address.getAddress(), add);
}

如果您允许地址依赖是可变的,则应该优先使用 setter,并且您可以在其中断言被测对象的之前/之后状态:

@Test
public void MockSampleUser(){
    MockSampleUser mockSampleUser = new MockSampleUser();
    // before setting the address
    assertEquals("Address", mockSampleUser.getAddress());

    // after setting the address
    MockSampleUser.Address address = mock(MockSampleUser.Address.class);
    mockSampleUser.setAddress(address);
    when(address.getAddress()).thenReturn("New Address");
    String add = mockSampleUser.getAddress();
    assertEquals("New Address", add);
    // or better, use the address object itself as expected
    assertEquals(address.getAddress(), add);
}

【讨论】:

  • 您可能会添加丑陋的其他选项,即使用 PowerMockito 或 JMockit 来模拟对 new 的调用。或者,注入模拟注释也应该起作用。但是,是的,所有这些都是丑陋的。
  • 你的最后一句话在右边。对于一个如此重要的问题来说太丑了:质量代码。不,我真的不能为可重构代码引用它们(无论好坏)。这里似乎是这样。
  • 老实说,我是为我自己做的:我会避免养成坏习惯,有一天我会在工作中找回自己去调试这种东西! :p
猜你喜欢
  • 2018-05-19
  • 2013-08-12
  • 1970-01-01
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多