Java 使用Mockito验证lambda上的方法调用

Java 使用Mockito验证lambda上的方法调用,java,unit-testing,lambda,junit,mockito,Java,Unit Testing,Lambda,Junit,Mockito,我试图验证并捕获lambda表达式中某些方法调用的参数,如下所示: public Optional<UserDetails> findOne(String userName) { String selectStatement = "SELECT * FROM users WHERE userName = :userName;"; return jdbi.withHandle(handle -> handle .creat

我试图验证并捕获lambda表达式中某些方法调用的参数,如下所示:

public Optional<UserDetails> findOne(String userName) {
    String selectStatement =
        "SELECT * FROM users WHERE userName = :userName;";

    return jdbi.withHandle(handle -> handle
            .createQuery(selectStatement)
            .bind("userName", userName)
            .map(new UserDetailsMapper())
            .findOne());
  }
例如,我想验证是否已使用
“userName”
和从我的
findOne
方法传入的
userName
参数字符串调用了
.bind()

感觉上我是在单元测试Jdbi类而不是我自己的类,但是我觉得应该测试
.createQuery()
.bind()
.map()
的参数,因为开发人员可能会意外地更改这些参数

我目前采用的方法是创建一个内存中的数据库并测试实际返回的内容,但这感觉更像是一个集成测试,而不是单元测试。我也不关心Jdbi
.withHandle()
方法实际返回的是什么,因为那时我基本上是在测试一个库

我的理解是,我应该对传递给
.withHandle()
(在本例中为lambda表达式)的参数进行单元测试,这正是我在这里要做的

我得到的最接近的方法是将逻辑移到方法引用中,但这不起作用,因为
userName
被传递到我的
findOne()
方法中,然后在lambda中使用

我还玩过Mockito的
doAnswer
,但没用


我只能考虑使用helper方法创建一个新类,以返回
userName
selectStatement
,等等,并验证是否调用了它们,但它感觉没有必要,只是为了提供可测试性而添加它。

lambda表达式是findOne方法实现的一部分。因此,您应该将其作为测试findOne的一部分进行测试。但在调用findOne时不会执行它。因此,为了能够测试它,执行findOne并对其进行验证和断言是不够的

我将模拟jdbi,然后使用捕获lambda函数的ArgumentCaptor对调用进行验证。然后可以将该lambda函数作为参数传递给测试lambda函数的测试方法。该方法看起来与任何其他测试方法相同,只是它永远不能作为单独的测试用例运行


在这种情况下,我认为一种这样的测试方法就足够了。但有时在测试lambda函数时,您可能需要创建并调用多个测试方法,以使用不同的输入值测试捕获的lambda函数。

您显示的代码主要由与各种jdbi类的交互控制。这样的代码应该用集成测试而不是单元测试来测试,因为在交互中有许多潜在的错误:您是否以正确的顺序调用了正确的函数,参数是否以正确的顺序和格式调用,以及每次调用的结果是否与您预期的一样(包括可能的异常)?

为了识别这些潜在的bug,集成测试是正确的方法:模拟环境不会帮助您找到这些bug。您将根据自己的理解(也就是说,根据自己潜在的误解)来实现mock

作为一个例子,考虑你的测试目标,你想测试<代码>绑定<代码> >用<代码>“用户名”<代码>和<代码>用户名< /代码>参数:如果你嘲笑<代码> BION/COD>,那么你嘲弄的<代码> BIG/<代码>将测试<代码>“用户名”< /C> >作为第一个参数,其次是

userName
的内容。但是,这并不能告诉您多少,因为您假定的顺序可能是错误的,或者因为
SELECT
语句中的变量名可能仍然与
SELECT
语句中的变量名不同,因此实际的
绑定将失败,等等


这样说,我同意Stefan Mondelaers的观点,lambda表达式属于<代码> FordNo./Case>方法,并且应该与它一起测试——只是我将用集成测试直接测试它,而不在这个特定的情况下在单元测试中做任何努力。也许对于某些特定的corrner情况,您不应该针对实现进行测试,因为实现会使测试与实现代码耦合。换句话说,当其他用户在生产代码中更改参数

userName
时,您必须更改测试代码,这导致测试过度依赖于实现。
public interface HandleCallback<T, X extends Exception> {
  T withHandle(Handle handle) throws X;
}