首页 > 解决方案 > 如何在构造函数中模拟实例?

问题描述

我有一堂课

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 时它会返回“新地址”。

标签: javajunitmockingmockitojunit5

解决方案


模拟旨在模拟被测对象的依赖关系,而不是模拟实现细节。
实际上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);
}

推荐阅读