I need to test the following code using JUnit. It looks complex to me and I am not even sure where to take a start from.
I know what the method is doing but I am unable to write a JUnit test for it. Do we follow a pattern or keep some key points in mind while testing any piece of code.
protected WebResource.Builder applyHeaders(WebResource service, List<? extends BaseClientHeader<?>> headers, List<HttpRequestClientHeader> httpHeaders) {
WebResource.Builder wrb = service.getRequestBuilder();
if( headers != null ) {
for( BaseClientHeader<?> header : headers ) {
wrb = wrb.header( ((IEnum)header.getName()).value(), header.getValue() );
}
}
if( httpHeaders != null ) {
for( HttpRequestClientHeader header : httpHeaders ) {
wrb = wrb.header( header.getName().value(), header.getValue() );
}
}
return wrb;
}
Thanks,
Even when this method looks like it does many different things and interacts lots of other code it should be rather simple to test; that’s because it only operates on objects that you hand in. Let’s see…
#Test
public void requestBuilderIsReturned() {
WebResource webResource = Mockito.mock(WebResource.class);
WebResource.Builder webResourceBuilder = mock(WebResource.Builder.class);
when(webResource.getRequestBuilder()).thenReturn(webResourceBuilder);
WebResource.Builder createdBuilder = objectUnderTest.applyHeaders(webResource, null, null);
assertThat(createdBuilder, is(webResourceBuilder));
}
That was pretty straight-forward. In order to verify correct operation on both kinds of headers you need to get a bit tricky, I suppose:
when(webResourceBuilder.header(anyString(), anyString())).thenReturn(webResourceBuilder);
This will simply make the header() method return the object it’s called upon. After that it should be quite simple to verify that the correct methods were called:
verify(webResourceBuilder).header("header1", "value1");
verify(webResourceBuilder).header("header2", "value2");
Armed with this you should be able to unit test the shit out of this particular method. :)
Related
I have one code snippet, which is calling 2 different services based on some a if condition. And both the services return CompletableFuture<Optional<SomeObject>>. Following is the code logic looks like
if(someCondition){
CompletableFuture<Optional<SomeObjectType1>> = service1.call();
}else{
CompletableFuture<Optional<SomeObjectType2>> = service2.call();
}
And both SomeObjectType1 and SomeObjectType2 have a String inside it, which is of my interest. My current code looks like this:
private ContentWrapper getContentWrapper(input1, input2, ....) {
String content = null;
if (some_condition is true) {
List<Object_Type_1> list = service1.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.orElse(null);
if (CollectionUtils.isNotEmpty(list)) {
content = list.get(0).getContent();
}
} else {
content = service2
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(RenderedContent::getContent)
.orElse(null);
}
return content != null ? new ContentWrapper(content) : null;
}
Now my question is, can this if-else clause be removed or make it more clear by using lambdas. I am new in lambdas and does not have very good idea on this.
I am not sure whether the code below even compiles due to the vagueness.
private ContentWrapper getContentWrapper(input1, input2, ....) {
Optional<RenderedContent> content = some_condition
? service1
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.stream()
.findFirst()
: service2
.fetchTheCompletableFuture(..... inputs...)
.join();
}
return content
.map(RenderedContent::getContent)
.map(ContentWrapper::new).orElse(null);
}
The first service seems to yield a list of RenderedContent of which to take the first if there is one.
The second service may yield a Rendered content immediately.
So you can join the if-else to an Optional<RenderedContent>.
The map(RenderedContent::getContent) will yield Optional.empty() if it was empty to begin with. Otherwise getContent is called and wrapped in an Optional.
If present new ContentWrapper(...) might be called.
Notice much may fail, like getContent returning null (though there is an Optional.ofNullable.
Nevertheless Streams may be very expressive.
I would avoid using null in favor of Optional as that plays better together.
So let us say we have something like:
public class SomeService {
...
public Flux<String> getStringsFromWebServer() {
return webClient.get()
.uri(this::generateSomeUrl)
.retrieve()
.bodyToMono(SomePojo.class)
.map(SomePojo::getStringList)
.flatMapMany(Flux::fromIterable);
}
Does it make sense to write tests that look like this:
void getStringsFromWebServer_shouldParseInOrderOfReceivingStrings() {
// given
// I have mocked up a WebClient, that is wired up to a Mocked Web Server
// I am preloading the Mocked Web Server with this JSON
String jsonStrings = "{'stringList': ['hello1', 'hello2', 'hello3']}"
mockWebServer.enqueue(new MockResponse().setResponseCode(200))
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody(jsonStrings);
// when
Flux<String> result = someService.getStringsFromWebServer();
// then
StepVerifier.FirstStep<String> fluxStep = StepVerifier.create(result);
for (int i = 1; i < 4; i++) {
String expectedInput = String.format("hello%d", i);
fluxStep.assertNext(someString -> assertEquals(expectedInput, someString));
}
fluxStep.verifyComplete();
}
Is this how you are to programmatically assert the order that comes back out from Flux?
Am I doing something bad with the assertNext flux method? I mean in this sense, I am always providing ordered data so I am assuming that fromIterable will consuming from that list in the order that it is received by the spring boot application.
It feels like I am violating some sort of principle here...I mean it works...
Meh sorted it out.
So there is the expectNext method:
https://www.baeldung.com/flux-sequences-reactor
Where you can pregenerate your list and then assert something like this:
List<String> expectedStrings = Arrays.asList(
"hello1", "hello2", "hello3"
);
...
StepVerifier.create(result)
.expectNextSequence(expectedStrings)
.verifyComplete();
EDIT: apparently I have to wait a few days before I can mark my own question as answered?
I am trying to mock my following java method using Spock.
public List<DiffEntry> listDifferences(String oldCommit, String newCommit, Git git)
throws GitAPIException, RevisionSyntaxException, AmbiguousObjectException,
IncorrectObjectTypeException, IOException {
logger.info(
"Inside DiffCommits#listDifferences to calculating difference commits refs {} and {} ",
oldCommit, newCommit);
ObjectId oldTree = git.getRepository().resolve(oldCommit);
ObjectId newTree = git.getRepository().resolve(newCommit);
if (oldTree == null || newTree == null) {
logger.warn(
"Could not resolve either old {} or new commits {}, difference cant not be calculated",
oldCommit, newCommit);
throw new RefNotFoundException("Unable to resolve tag reference. Invalid tag provided");
}
ObjectReader reader = git.getRepository().newObjectReader();
CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
oldTreeIter.reset(reader, oldTree);
CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
newTreeIter.reset(reader, newTree);
DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
df.setRepository(git.getRepository());
List<DiffEntry> entries;
entries = df.scan(newTreeIter, oldTreeIter);
df.close();
if (logger.isDebugEnabled()) {
for (int i = 0; i < entries.size(); i++) {
logger.debug("Entry: " + entries.get(i));
}
}
return entries;
}
Everything is working fine but the mocking of following code get failed
DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
And getting error at
df.setRepository(git.getRepository());
The error that I am getting is
> org.eclipse.jgit.lib.StoredConfig$$EnhancerByCGLIB$$9a2f8398 cannot be
> cast to org.eclipse.jgit.diff.DiffConfig java.lang.ClassCastException:
> org.eclipse.jgit.lib.StoredConfig$$EnhancerByCGLIB$$9a2f8398 cannot be
> cast to org.eclipse.jgit.diff.DiffConfig at
> org.eclipse.jgit.diff.DiffFormatter.setReader(DiffFormatter.java:201)
> at
> org.eclipse.jgit.diff.DiffFormatter.setRepository(DiffFormatter.java:180)
> at
> com.sf.bt.mdm.subscription.scmdiff.DiffCommits.listDifferences(DiffCommits.java:65)
> at com.sf.bt.mdm.subscription.service.DiffCommitSpec.test list
> differences(DiffCommitSpec.groovy:59)
Any kind of help will be appreciated
DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
This code is problematic from the point of view of unit testing. Its not a dependency so it can't be injected in the class. So basically you're trying to mock a constructor (new) object.
Technically the only way you can try is using Global Mocks and create spies wrapping the real object: Read the documentation. These are not regular spock's Mocks and Stubs.
However I believe there are other approaches. After all the link I've sent you starts with "think twice before using..." for a reason ;)
You've by yourself decided to put this object creation inside the method, which is ok. However this means that you do not treat this DiffFormatter as a real dependency. So maybe even in unit test you should let it run, concentrating on mocking its input parameters themselves? Just the same holds also for ByteArrayOutputStream. This plays nicely in general with the concept that the code should be written in a way that unit tests should not really care about the internal implementation of the methods. Always prefer blackbox testing to white-box testing.
Alternatively if you think its a dependency, then maybe you should make it a data field. Then You could inject it into the test with a regular mocks. If this class should be created per method invocation, you can inject Supplier<DiffFormatter> and in method just call it's get method, it will act as a factory.
I'm making a test for a service with a mock.
The problem is to create and inject instance directly from the class to test.
The source is shown below.
public OrderOutDTO createOrder(OrderSessionDTO orderSessionDTO) {
Order order = orderRepository.save(new Order(orderSessionDTO));
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
CreateOrderReqDTO createOrderReqDTO = mock(CreateOrderReqDTO.class);
createTrace(order, callServiceOrder.getData().getReceipt().getTransactionHash(), Trace.PUBLIC);
return new OrderOutDTO(order, null);
}
and test source is shown below.
#Test
public void createOrder() {
// given
CallService callService = mock(CallService.class);
CreateOrderResDataDTO createOrderResDataDTO = mock(CreateOrderResDataDTO.class);
// when
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
// then
assertThat(order, is(Matchers.notNullValue()));
assertThat(order.getOrder(), is(Matchers.notNullValue()));
assertThat(order.getOrder().getReceiver().getName(), is("test"));
}
I thought this test would finish well. But in the code below, it returned null and failed.
// callService.createOrder(new CreateOrderReqDTO(order)) return null
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
It doesn't seem to recognize it because the service injects a new instance. I want the mock data returned. What should I do?
In the following line you're mocking behavior on createOrderReqDTO as param:
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
whereas further, you're passing some other object:
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
This behavior is not recognized, you would have to pass the same thing you mocked before.
I found it myself!
I use argumentMatchers.
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
to
when(callService.createOrder(any())).thenReturn(createOrderResDTO);
thank you.
I want to implement a typical rest POST call in Lagom. The POST creates an object, and returns it, with a status code of 201.
However, the default return code is 200. It is possible to set a status code, as shown here (https://www.lagomframework.com/documentation/1.3.x/java/ServiceImplementation.html#Handling-headers).
However, I cannot figure out how to do it for a more complicated case. My create is asynchronious, and I return an object instead of a String.
This is the code I have:
#Override
public HeaderServiceCall<OrderRequest.CreateOrderRequest, Order> createOrder() {
UUID orderId = UUID.randomUUID();
ResponseHeader responseHeader = ResponseHeader.OK.withStatus(201);
return (requestHeader, request) -> {
CompletionStage<Order> stage = registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply(reply -> toApi(reply));
return CompletableFuture.completedFuture(Pair.create(responseHeader, stage.toCompletableFuture()));
};
}
However, the return value should be Pair<ResponseHeader, Order>, not Pair<ResponseHeader, CompletionStage<Order>> which I have now, so it does not compile.
I could of course extract the Order myself, by putting the completionStage into an CompletableFuture and getting that, but that would make the call synchronous and force me to deal with InterruptExceptions etc, which seems a bit complex for something that should be trivial.
What is the correct way to set a status code in Lagom?
You almost have it solved. Instead of creating a new completedFuture you could compose stage with a lambda that builds the final Pair like this:
return stage.thenApply( order -> Pair.create(responseHeader, order));
And putting all the pieces together:
registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply( reply -> toApi(reply));
.thenApply( order -> Pair.create(responseHeader, order));