【问题标题】:Mockito keeps returning Empty ListMockito 不断返回空列表
【发布时间】:2018-09-25 12:00:25
【问题描述】:

我正在对 Mockito 中的一个方法进行单元测试,即使我已经初始化了要返回的列表,mockito 也会继续发送一个空的零大小列表。

这是要测试的代码。请注意,nonCashIncludedPaymentPlanActive 始终为 true ( Mocked )。

    List<DebtAccountTransaction> debtAccountTransactionList = null;

    boolean nonCashIncludedPaymentPlanActive = balancingPlanService.checkNonCashIncludedPaymentPlanParameter(debtAccountId);


    if (nonCashIncludedPaymentPlanActive) {
        debtAccountTransactionList = debtAccountTransactionDao
                .getDebtAccountTransactionListByDebtAccountIdListWithCN(baseDebtIdAccountList, null);
    } 
    if (debtAccountTransactionList.isEmpty()) {
        throw new SfcException("DISPLAY.PAYMENT_PLAN_WITH_NO_BALANCE_SERVICE_FILE_CLOSED");
    }

这条语句不断返回我在 mockito 中模拟的 List 并向其中添加了一个项目,在这里它返回一个空列表。

debtAccountTransactionList = debtAccountTransactionDao
                .getDebtAccountTransactionListByDebtAccountIdListWithCN(baseDebtIdAccountList, null);

当然会被这条线抓住

if (debtAccountTransactionList.isEmpty()) {
        throw new SfcException("DISPLAY.PAYMENT_PLAN_WITH_NO_BALANCE_SERVICE_FILE_CLOSED");
    }

因此,为了避免这种执行路径,我在 Mockito 中做了以下操作:

when(debtAccountTransactionDao.getDebtAccountTransactionListByDebtAccountIdListWithCN(baseDebtIdAccountList, null)).thenReturn(
            debtAccountTransactionList);

debtAccountTransactionList 的声明是:

DebtAccountTransaction debtAccountTransaction = spy(DebtAccountTransaction.class);
    debtAccountTransaction.setId(2L);


    List<DebtAccountTransaction> debtAccountTransactionList = new ArrayList<DebtAccountTransaction>();
    debtAccountTransactionList.add(debtAccountTransaction);

我尝试模拟一个列表,尝试了不同的参数捕获器,但似乎没有任何效果。当我调试它时,Mockito 确实填充了 debtAccountTransactionList 但列表为空,因此它失败了。

任何有关如何确保 Mockito 发送非空非零列表以便它可以绕过 isEmpty() 检查的帮助。

【问题讨论】:

  • 问题不在于创建 Mocks,问题在于 debcountTransactionList 被返回为 Null 或零大小。
  • 当使用一个集合(看起来是这样)时,请确保该集合的类型相同,并且其中的元素具有正确的 equals/hashcode 实现。否则 Mockito 不会将其视为匹配项并返回一个空列表(默认行为)。
  • 问题是模拟创建/行为注册。这与您放入方法中的内容不匹配,因此返回到返回空列表的默认行为。
  • 能否请您发布整个测试类以及依赖项、注释?
  • 我明白了,我可以将它与传递给 debtAccountTransactionDao.getDebtAccountTransactionListByDebtAccountIdListWithCN(baseDebtIdAccountList, null) 的任何 List 参数相匹配 我的意思是而不是 baseDebtIdAccountList,我想要它接受任何长列表。我尝试使用参数匹配器,但它们似乎不起作用并导致错误。我在代码中还有一份 baseDebtIdAccountList 的副本,所以有什么方法可以检查以确保 baseDebtIdAccountList 等于我传入的 baseDebtIdAccountList。

标签: java spring unit-testing mockito


【解决方案1】:

编写测试的一个很好的经验法则,尤其是对于像 Mockito 这样的模拟库:不要将存根与验证混淆。存根 (when) 是关于让您的被测系统 (SUT) 进入所需状态,不是关于断言任何关于 SUT 行为的信息。

在 Mockito 中,对 SUT 的行为方式进行断言的方法是 SUT 运行之后,使用verify 调用。如果您没有任何 verify 调用,那么您实际上并没有断言任何东西,并且您的 SUT 可能会在您的测试没有捕捉到它的情况下出现错误,这显然是一件坏事。

因此,通常最好使您的存根匹配器 (when) 尽可能广泛,因为存根的目标只是确保您落入正确的测试用例。例如,您可以并且经常应该在您的 when() 调用中使用像 any() 这样的匹配器。如果你这样做了,你就会回避你在这里遇到的问题。

如果您想对 SUT 实际用作参数的值进行断言,请使用 verify 执行此操作,或者可能通过捕获该值并直接对其进行附加断言。

【讨论】:

  • 你是绝对正确的。我尝试使用 any() 或 anyListOf() 或其他匹配器,但我无法理解正确使用它作为 Mockito 的语法,如果你有任何资源来理解它的语法,那会很棒。是的,我在准备 SUT 后会进行验证调用,并且我会尽可能广泛地记录我的 SUT 准备情况。另外,如果你有一本关于 mockito 或单元测试的书可以推荐,那将非常有帮助。谢谢。
  • 那里有很多书;我没有一个好的建议。如果您有 any() 向您发出编译器警告或错误的特定情况,请随时在 Stack Overflow 上询问相关人员。更一般地说,学习 Java 如何在编译时推断和解析类型是很有价值的。我绝对建议您阅读您正在使用的 Java 版本的 Java 语言规范——阅读内容有点密集,但我认为它并不难理解,它是权威参考。
  • 同样值得注意的是,尽管第一次出现,Mockito 并不是“神奇的”:所有 Mockito 对象和函数都遵循该语言中任何其他对象的相同规则和限制。他们只是以隐蔽的方式相互交流,给人一种“魔法”的错觉。一旦你明白了什么,例如when 正在做,不会那么混乱。
  • 别担心,我肯定会询问有关 Stack Overflow 的任何错误,并将通过规范! :)
  • 我也强烈推荐阅读 Martin Fowler 的文章 Mocks Aren't Stubs 以获得有关该主题的良好背景。
【解决方案2】:

问题在于模拟创建/行为注册。这与您放入方法中的内容不匹配,因此返回到返回空列表的默认行为。

正如M. Deinum指出的那样

因此,Mockito 接受参数时出现问题,它会忽略我的存根,然后默认返回一个空列表。

我通过确保将 baseDebtIdAccountList 的对象传递给函数 when(debtAccountTransactionDao.getDebtAccountTransactionListByDebtAccountIdListWithCN(baseDebtIdAccountList, null)).thenReturn( debcountTransactionList) 在其余代码中完全相同。因此,参数不匹配,Mockito 使用了使用空列表的默认方式。

【讨论】:

    【解决方案3】:

    你是否在代码的任何地方放了类似的东西:

    debtAccountTransactionDao = Mockito.mock(NameOfTheClassOfThisDebtAccountObject.class);
    

    ?

    您应该在调用方法getDebtAccountTransactionListByDebtAccountIdListWithCN 之前放置这样的内容,因此它知道它应该使用模拟行为,而不是该方法的正常行为(可能返回一个空列表)。

    【讨论】:

    • 是的,已经完成了,但这不是问题的根源 :)
    • 谢谢Matteo,对初学者非常有用的信息:)
    猜你喜欢
    • 1970-01-01
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 2020-12-02
    • 1970-01-01
    相关资源
    最近更新 更多