Reputation: 35
I am writing a unit test for functionA(). My problem is that I don't know if there exists a way to trigger the lambda's apply() which is in functionC() of DependentClass. The lambda lambdaFunction is a functional interface that stores call to functionB().
SystemUnderTest:
public class SystemUnderTest {
private DependentClass dependentClass;
public void functionA() {
LambdaFunction lambdaFunction = (Foo foo,
Bar bar) -> {
functionB(foo, bar); // needs to execute by triggering apply() on lambdaFunction. i don't know of a way to explicitly call functionB() without triggering it.
};
dependentClass.functionC(lambdaFunction); // needs to be called so that apply() is triggered, so should i be mocking this call?
}
private void functionB(Foo foo,
Bar bar) {
.
.
.
}
}
DependentClass that needs to be mocked out:
public class DependentClass {
public void functionC(LambdaFunction lambdaFunction) {
lambdaFunction.apply(foo, bar); // it would be nice to invoke apply() so that functionB() executes.
}
}
Functional interface whose implementation stores the to call to functionB():
@FunctionalInterface
public interface LambdaFunction {
void apply(Foo foo,
Bar bar);
}
JUnit raw test:
public class ClassUnderTestJUnitTest {
@Mock
private DependentClass dependentClassMock;
@Mock
private LambdaFunction lambdaFunctionMock;
@Test
public void test_functionA() {
Mockito.doNothing().when(dependenClassMock).functionC(lambdaFunctionMock);
SystemUnderTest systemUnderTest = new SystemUnderTest(dependenClassMock);
systemUnderTest.functionA();
}
}
However, as you can see I don't know if there exists a way such that I can invoke functionB(). Ideally, it'd be very helpful if I could make a real method call (I don't know if this is a good practice though) for dependentClass.functionC(lambdaFunction) which would then trigger lambdaFunction.apply(foo, bar) resulting in calling functionB(foo, bar). My apologies if I didn't explain the problem statement well. I'd appreciate any help or hints on how to achieve writing the unit test/s for this scenario.
Upvotes: 2
Views: 794
Reputation: 30676
You have 2 choices to verify the LambdaFunction
whether is called or not.
Don't inject a Test Double but a real implementation of the DependentClass
into the SystemUnderTest
, and then verify the affected states after call functionA
in SystemUnderTest
. for example:
@Test
public void test_functionA() {
// use a real collaborator if possible ---v
SystemUnderTest systemUnderTest = new SystemUnderTest(new DependentClass());
systemUnderTest.functionA();
// assert the SUT affected states
assertEquals(expectedState, systemUnderTest.affectedState);
}
Behavior based verification is testing the communication between SUT and its collaborators, so you can't test it by a single test. Indeed, you need 2 separated tests for that. one is test the communication, another is test the collaborator is whether fulfill its contract.
@RunWith(MockitoJUnitRunner.class)
public class ClassUnderTestJUnitTest {
@Mock
private DependentClass dependentClassMock;
@Test
public void test_functionA() {
SystemUnderTest systemUnderTest = new SystemUnderTest(dependentClassMock);
systemUnderTest.functionA();
// v--- verify its communication
verify(dependentClassMock, only()).functionC(any(LambdaFunction.class));
}
}
@RunWith(MockitoJUnitRunner.class)
public class DependentClassTest {
private final DependentClass dependentClass = new DependentClass();
private @Mock LambdaFunction function;
@Test
public void test_functionA() {
dependentClass.functionC(function);
// v--- verify it whether fulfill its contract
verify(function, only()).apply(any(Foo.class), any(Bar.class));
}
}
For more details, you can see martinfowler's blog: Mocks aren't Stubs.
Upvotes: 4