Test lambda expressions called by dependencies

夙愿已清 提交于 2021-02-18 11:47:31

问题


I am trying to test some code inside lambda expression which is a call back from another class.

class EmailSender {
    private EmailBuilder emailBuilder;

    public void send() {
        String testEmail = emailBuilder.buildEmail("Test Email", bodyContentAppender());
        //send testEmail
    }

    private Consumer<Email> bodyContentAppender() {
        //how to test this through JUnit?
        return email -> email.appendBody("Body Content");
    }
}

interface EmailBuilder {

    String buildEmail(String templateName, Consumer<Email> contentAppender);
}

The lambda expression in the method getBodyContent is called from EmailBuilder which is a mocked dependency in the JUnit test for EmailSender. Since I am mocking the behavior of EmailBuilder, the code inside getBodyContentis not called from tests. How to test such piece?

EDIT: Capturing the lambda expression through Argument Captors is not a solution in this case as the behavior of EmailBuilder is mocked and the actual methods are not called. Secondly, email.appendBody does some transformations on an object which is passed by an external API and not straightforward to create.


回答1:


What you are trying to do here is essentially to verify that a factory method did in fact really return the correct object. There is this related question, where the consensus is to not test the result of a factory method beyond verifying that it does indeed return an object of the correct type. The behavior of that object should be tested in the UnitTests for that type.

In an answer to this related question on unit testing lambdas Stuart Marks argues that

If the code in the lambda is complex enough that it warrants testing, maybe that code ought to be refactored out of the lambda, so that it can be tested using the usual techniques.

Now, the real question is: If this was not a lambda, but a concrete class MyBodyContentAppender that implements the functional interface Consumer<Email>, how would you unit test that? What kinds of test would you write for this class?

You would probably write tests to verify that, given an Email, invoking accept() does indeed invoke appendBody() with the appropriate parameters, perhaps that invoking it with a null argument throws a NullPointerException etc. You would possibly not verify that email.appendBody() works as expected, because that is covered by the tests for Email. You may have to mock Email for these tests if it is difficult to create.

Well, all of these tests can also be performed for the lambda. Your problem is that the factory and the type of the created object are both private, so from the perspective of your test, the only way to access that object is via the parameter passed to the (mocked) emailBuilder.buildEmail().

If you use Mockito for mocking the emailBuilder, you could capture the arguments to this method via ArgumentCaptors (see 15. Capturing arguments for further assertions (Since 1.8.0)), I'm sure other mocking libraries provide similar functionality.




回答2:


Most mocking frameworks allow you to check arguments that are used when invoking methods on mocked object. Respectively, you can capture them.

So:

  • acquire the parameter passed
  • simply invoke the "code" that it represents, and check if that makes the expected updates to an Email object you provided.



回答3:


It will be easier to test if you actually supply the body content as an argument, and make sure the method is public. If you intend to keep the method as private, then you should test the public method calling it.

public Consumer<Email> getBodyContent(String body) {
    //how to test this through JUnit?
    return email -> email.appendBody(body);
}

Then you can test it as

@Test
public void testGetBodyContent(){
    .... //send different arguments and assert
    ....
}



回答4:


So when you want to unit test your code you must ensure that it's doing one job at a time. You method is named as getBodyContent but seems like is supposed to do no more work than appending to email body. Hence, you could pull out this method to be public.

Now you could pass the body and get the content.

@Test
public void testGetBodyContent(){

consumer<Email> consumer = EmailSender.getBodyContent();

assertEquals("Email Content", consumer.accept(<mocked Email object>).getBody())
}


来源:https://stackoverflow.com/questions/54308063/test-lambda-expressions-called-by-dependencies

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!