问题
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 getBodyContent
is 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 ArgumentCaptor
s (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