The context is development on a Jenkins shared library. Jenkins exposes some functionality as pipeline steps which take closures. I cannot call these steps at test-time, so wrap and then mock them. But I reckon the situation can be generalized to the following:
You have a class-under-test with two dependencies.
class SystemUnderTest {
DependencyOne firstDependency
DependencyTwo secondDependency
public SystemUnderTest(DependencyOne firstDependency, DependencyTwo secondDependency) {
this.firstDependency = firstDependency
this.secondDependency = secondDependency
}
public void methodUnderTest(Integer a, Integer b) {
Integer sum = a+b
firstDependency.methodWhichTakesClosure() {
// important behaviour that needs to be verified
secondDependency.call(sum)
}
}
}
class DependencyOne {
public methodWhichTakesClosure(Closure c) {
// <<Insert some calls with external effects>>
c.call()
}
}
class DependencyTwo {
public void useResult(Int i) {
// Do something with i
println(i)
}
}
A particular method-under-test prepares some data to pass to a method on the second dependency. That call has to be within a closure passed to a method of the first dependency as it relies on the context provided.
The question is how can the behaviour of the method-under-test be verified?
If the first dependency didn't have to be mocked, I would just verify that the method on the second dependency was called with the appropriate data, but obviously that won't work because the closure isn't actually called.
My second inclination is to reproduce the closure and verify that the method on the first dependency is called with that closure, but this doesn't seem possible - the closures are different. I'll confess to an incomplete understanding how closures work behind the scenes, but from what I do understand, the prepared closure and the actual closure capture different contexts so are not identical.
@RunWith(MockitoJUnitRunner.class)
class TestClass() {
SystemUnderTest sut
@Mock DependencyOne firstDependency
@Mock DependencyTwo secondDependency
@Before
void setup() {
sut = new SystemUnderTest(firstDependency, secondDependency)
}
@Test
void verify_methodUnderTest() {
sut.methodUnderTest(1,2)
// ATTEMPT 1: This is never called as first dependency is mocked
verify(secondDependency).useResult(3)
// ATTEMPT 2: This won't work as closure is different
verify(firstDependency).methodWhichTakesClosure {
secondDependency.call(3)
}
}
}
So I'm looking for a way to achieve verification of behaviour of the method under test in this scenario.