Java 单元测试与行为相关的私有字段

Java 单元测试与行为相关的私有字段,java,testing,junit,mocking,Java,Testing,Junit,Mocking,首先:我发现我的问题几乎没有解决方案,但“最新”的解决方案是2014年的,并且使用了反思,所以我希望我可以为我的问题找到一些更高级的解决方案 这是关于migrateUser和canAdd的情况。这是一个示例类,使我的问题更容易看到 public class UserInterfaceImpl implements UserInterface { private final List<T> accountList = new LinkedList<>();

首先:我发现我的问题几乎没有解决方案,但“最新”的解决方案是2014年的,并且使用了反思,所以我希望我可以为我的问题找到一些更高级的解决方案

这是关于
migrateUser
canAdd
的情况。这是一个示例类,使我的问题更容易看到

public class UserInterfaceImpl implements UserInterface {

    private final List<T> accountList = new LinkedList<>();
    private final AccountInterface accountInterface;
    private boolean bonusReceived = false;

    public UserInterfaceImpl(AccountInterface accountInterface) {
        this.accountInterface = accountInterface;
    }

    public void migrateUser(AccountMergerInterface accountMerger, UserInterface oldUser) {
        boolean success = accountMerger.performChange(this, oldUser);
        if (success && !bonusReceived) {
            //addBonus
            accountInterface.deposit(1);
            bonusReceived = false;
        }
    }

    public boolean canAdd() {
        return accountList > 0;
    }

    public AccountInterface getAccount() {
        return accountInterface;
    }
}
但现在我不能继续了。在我的示例中,规则应该是没有getter和setter!这门课应该像我的例子一样

我不知道如何:

  • 在执行
    accountInterface.deposit(1)之前,将
    bonusReceived
    设置为false未执行
  • 查看如果
    if()
    continue为true,则
    bonusReceived
    是否将设置为false
  • 列表的问题也一样:如何访问列表的私有字段,添加对象以使返回值为true或false。或者我应该“模拟”一个列表,如果是,我该怎么做
  • 提前谢谢

    单元测试检查公众可观察的行为

    验证对象的内部状态没有意义,因为这可能会更改以支持其他行为。在这种情况下,您不想更改单元测试

    因此,模拟
    AccountMergerInterface
    (正如您已经做的那样)和
    AccountInterface
    ,并验证您的被测类是否以正确的顺序使用正确的参数对它们调用方法:

    @Rule 
    public MockitoRule mockitoRule = MockitoJUnit.rule(); 
    @Mock
    private AccountMergerInterface accountMergerInterface;
    @Mock
    private AccountInterface accountInterface;
    @Test
    public void testMigrateUser() {
        // arrange
        when(test.performChange()).thenReturn(true);
        // act
        new UserInterfaceImpl(accountInterface).migrateUser(accountMergerInterface);
        // assert
       InOrder inOrder Mockito.inOrder(accountMergerInterface, accountInterface);
       inOrder.verify(accountMergerInterface).deposit(1);
       inOrder.verify(accountInterface).whatEverToCallNext();
    }
    
    列表中的问题相同:

    当前没有处理
    userinterfaceeimpl
    中列表的代码。 您无法验证不存在的行为…

    单元测试检查公共可观察行为

    验证对象的内部状态没有意义,因为这可能会更改以支持其他行为。在这种情况下,您不想更改单元测试

    因此,模拟
    AccountMergerInterface
    (正如您已经做的那样)和
    AccountInterface
    ,并验证您的被测类是否以正确的顺序使用正确的参数对它们调用方法:

    @Rule 
    public MockitoRule mockitoRule = MockitoJUnit.rule(); 
    @Mock
    private AccountMergerInterface accountMergerInterface;
    @Mock
    private AccountInterface accountInterface;
    @Test
    public void testMigrateUser() {
        // arrange
        when(test.performChange()).thenReturn(true);
        // act
        new UserInterfaceImpl(accountInterface).migrateUser(accountMergerInterface);
        // assert
       InOrder inOrder Mockito.inOrder(accountMergerInterface, accountInterface);
       inOrder.verify(accountMergerInterface).deposit(1);
       inOrder.verify(accountInterface).whatEverToCallNext();
    }
    
    列表中的问题相同:

    当前没有处理
    userinterfaceeimpl
    中列表的代码。
    您无法验证不存在的行为…

    重构代码是一个选项吗?一个选项是对类的外部可见行为进行单元测试。这样做的好处是,即使私有实现发生更改,单元测试仍然有效,这是单元测试的要点之一。如果您真的想测试私有实现的各个方面,一种方法是提供受包保护的成员,并将单元测试放在同一个包中。您不希望您的单元测试知道这个私有布尔值。您只想测试给定的“此”输入“这一结果显示了出来。如何实现它不应该是测试的一部分。还是每次重构生产代码时都要更改测试?!重构代码是一种选择吗?一种选择是对类的外部可见行为进行单元测试。这样做的好处是,即使私有实现发生更改,单元测试仍然有效,这是单元测试的要点之一。如果您真的想测试私有实现的各个方面,一种方法是提供受包保护的成员,并将单元测试放在同一个包中。您不希望您的单元测试知道这个私有布尔值。你只想测试结果显示的给定“这个”输入。如何实现不应该是你测试的一部分。或者你打算在每次重构生产代码时更改你的测试吗?!啊,好的,这意味着如果我有方法public void addElement(T element){list.add(element)}我可以在@Test方法中使用它来验证canAdd()是否正确是否正常工作?如果是这样,我在书中理解的规则是错误的:)我认为约定是,我应该始终只使用我测试的方法,而不访问我测试的任何其他类,以防止出现副作用错误。感谢您的回答!如果该方法是类API的一部分,那么您应该使用它,因为它是类的一部分您验证的行为。您应该避免向类中添加仅用于测试的代码。唯一有效的异常是在被测试的类中实例化的依赖项的(包私有)getter,该依赖项不能更改为注入。啊,好的,这意味着如果我有public void addElement(T元素){list.add(element)}我可以在@Test方法中使用它来验证canAdd()是否正常工作?如果是这样,我在书中理解的规则是错误的:)我认为约定是,我应该始终只使用我测试的方法,而不访问我测试的任何其他类,以防止出现副作用错误。谢谢你的回答!如果该方法是类API的一部分,那么你应该使用它,因为它是你验证的类行为的一部分。你应该避免向你的类添加代码hch仅用于测试。唯一有效的异常是依赖项的(包专用)getter,该依赖项在被测试的类中实例化,不能更改为注入。