λ13
λ13

Reputation: 35

Invoking a method from a lambda that is a part of system under unit test/s

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

Answers (1)

holi-java
holi-java

Reputation: 30676

You have 2 choices to verify the LambdaFunction whether is called or not.

State Based Verification

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

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

Related Questions