Unit test код с Java 8 Lambdas

Я использую Java 8 в течение нескольких месяцев, и я начал использовать выражения Lambda, которые очень удобны для некоторых случаев. Тем не менее, я часто сталкиваюсь с некоторыми проблемами с unit test кодом, который использует Lambda.

Возьмем в качестве примера следующий псевдокод:

private Bar bar;

public void method(int foo){
    bar.useLambda(baz -> baz.setFoo(foo));
}

Один из подходов состоит в том, чтобы просто проверить вызов на панели

verify(bar).useLambda(Matchers.<Consumer<Baz>>.any());

Но, делая это, я не тестирую код Лямбды.

Также обратите внимание, что я не могу заменить Lambda на метод и использовать ссылку на метод:

bar.useLambda(This::setFooOnBaz);

Потому что у меня не будет foo для этого метода. Или, по крайней мере, это то, что я думаю.

У вас была эта проблема раньше? Как я могу проверить или реорганизовать свой код, чтобы проверить его правильно?


Edit

Так как я кодирую unit test, я не хочу создавать экземпляр бара, и вместо этого я буду использовать макет. Поэтому я не могу просто проверить вызов baz.setFoo.

Ответ 1

Вы не можете unit test lambda напрямую, так как у него нет имени. Нет способа вызвать его, если у вас нет ссылки на него.

Обычной альтернативой является рефакторинг лямбда в именованный метод и использование ссылки на метод из кода продукта и вызов метода по имени из тестового кода. Как вы заметили, этот случай нельзя реорганизовать таким образом, потому что он захватывает foo, и единственное, что может быть захвачено ссылкой метода, это приемник.

Но ответ от yshavit затрагивает важный вопрос о необходимости использования unit test частных методов. Лямбда, безусловно, может считаться частным методом.

Здесь тоже больше. Одним из примеров модульного тестирования является то, что вам не нужно unit test что-либо, что слишком просто перебить. Это хорошо согласуется с идеальным случаем для лямбда, который является выражением, которое так просто, очевидно, правильно. (По крайней мере, это то, что я считаю идеальным.) Рассмотрим пример:

    baz -> baz.setFoo(foo)

Есть ли какое-либо сомнение в том, что это лямбда-выражение при передаче ссылки Baz вызовет его метод setFoo и передаст его foo в качестве аргумента? Возможно, это так просто, что его не нужно тестировать на единицу.

С другой стороны, это всего лишь пример, и, возможно, фактическая лямбда, которую вы хотите протестировать, значительно сложнее. Я видел код, который использует большие, вложенные, многострочные лямбды. См. этот ответ и его вопрос и другие ответы, например. Такие лямбды действительно трудно отлаживать и тестировать. Если код в лямбде достаточно сложный, что он требует тестирования, возможно, этот код следует переделать из лямбда, чтобы его можно было протестировать с использованием обычных методов.

Ответ 2

Относитесь к лямбдам, как к частному методу; не проверяйте его отдельно, а скорее проверяйте его эффект. В вашем случае вызов method(foo) должен вызвать bar.setFoo, чтобы произойти - поэтому вызовите method(foo), а затем проверьте bar.getFoo().