【问题标题】:Unit Test a private field which is behavior relevant单元测试与行为相关的私有字段
【发布时间】:2017-04-26 16:56:44
【问题描述】:

首先:我为我的问题找到了一些解决方案,但“最新”的解决方案是从 2014 年开始并使用反射,所以我希望我可以为我的问题找到一些更高级的解决方案。

情况就是这样,关于migrateUsercanAdd。这是一个示例类,可以让我的问题很容易看到。

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;
    }
}

migrateUser 方法更改了一些与我的测试无关的帐户数据,因为我当然会单独测试它(应该像我目前所读的那样)。

所以我想知道,如何查看该类的行为是否正确更改了bonusReceived?不使用反射并尽可能复杂?

我的第一次尝试是:

@Test
public void testMigrateUser() {
    AccountMergerInterface test = mock(AccountMergerInterface.class);

    // define return value for method getUniqueId()
    when(test.performChange()).thenReturn(true);
}

但现在我无法继续。在我的示例中,规则应该是没有 getter 和 setter!这个类应该像我的例子。

我不知道该怎么做:

  1. bonusReceived设置为假之前 migrateUser被执行,accountInterface.deposit(1);执行
  2. 如果if() continue 为 true,则查看 bonusReceived 是否会设置为 false。
  3. List 的相同问题:如何访问 List 的私有字段,添加一个 Object 以便返回值为 truefalse。或者我应该“模拟”一个列表,如果是,我该怎么做?

提前致谢!

【问题讨论】:

  • 重构代码是一种选择吗?
  • 一种选择是对类的外部可见行为进行单元测试。这带来的好处是,即使私有实现发生变化,您的单元测试仍然有效,这是单元测试的要点之一。如果你真的想测试私有实现的各个方面,一种方法是提供包保护成员,并将单元测试放在同一个包中。
  • Timothoys 的回答很到位。您不希望您的单元测试知道这个私有布尔值。您只想测试给定的“此”输入“结果显示。如何实现不应该是您的测试的一部分。或者您是否打算在每次重构生产代码时更改您的测试?!

标签: java testing junit mocking


【解决方案1】:

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

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

所以模拟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();
}

对于列表的相同问题:

目前没有代码处理UserInterfaceImpl 中的列表。 您无法验证不存在的行为...

【讨论】:

  • 嗯,好吧,这意味着如果我有方法 public void addElement(T element){ list.add(element) } 我可以在我的@Test 方法中使用它来验证是否 canAdd()工作正常吗?如果是这样,我对书中的规则理解错误:) 我认为惯例是我应该始终只使用我测试的方法而不访问我测试的任何其他类,以防止副作用错误。感谢您的回答!
  • 如果该方法是类 API 的一部分,那么您应该使用它,因为它是您验证的类行为的一部分。您应该避免向您的类添加仅用于测试的代码。唯一有效的例外是(包私有)getter,用于在被测类中实例化的 dependency,不能更改为注入。
猜你喜欢
  • 2016-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 2011-08-20
  • 2013-09-01
  • 1970-01-01
  • 2014-09-08
相关资源
最近更新 更多