java: unit test lambda expression inside dispatcher - java

I have a class:
public class RequestHandler implements HttpHandler {
public void handleRequest(HttpServerExchange serverContext) throws Exception {
serverContext.dispatch(() -> serverContext.getRequestReceiver()
.receiveFullBytes((httpServerExchange, reqBytes) -> {
// business logic along with few function call
}
)
);
}
}
I want to write a unit test case to test my business logic. I am not sure how to do it with 2 levels of a lambda expression insider a dispatcher? Can someone please suggest a good way to write test cases?
I know that we can move business logic to new class and can test it (i guess it's better designed) but curious to know what if it's part of some legacy code or something that we can't change, how can we test it?

Under the assumption that somewhere in your buisness logic you forward the received message (or whatever you do with it) to somewhere else, you can just test your code as usual.
Note that HttpServerExchange is a final class, so you need to use a Mockito version that supports final mocking - and you have to enable it, as described here.
To get around the lambda expression you need to use thenAnswer or doAnswer to trigger the invocation of the correct interface method manually.
A simple example could look like this:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import io.undertow.io.Receiver;
import io.undertow.io.Receiver.FullBytesCallback;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
#ExtendWith(MockitoExtension.class)
public class RequestHandlerTest {
static class BuisnessLogic {
public void someMethod(HttpServerExchange httpServerExchange, byte[] reqBytes) {
}
}
static class RequestHandler implements HttpHandler {
BuisnessLogic logic;
public void handleRequest(HttpServerExchange serverContext) throws Exception {
serverContext.dispatch(
() -> serverContext.getRequestReceiver().receiveFullBytes(
(httpServerExchange, reqBytes) -> {
logic.someMethod(httpServerExchange, reqBytes);
}
)
);
}
}
#Mock
BuisnessLogic logic;
#InjectMocks
RequestHandler handler;
#Test
public void test() throws Exception {
byte[] message = new byte[] {1,2,3};
HttpServerExchange serverContext = Mockito.mock(HttpServerExchange.class);
// 1st lambda
Mockito.when(serverContext.dispatch(Mockito.any(Runnable.class)))
.thenAnswer((Answer<HttpServerExchange>) invocation -> {
Runnable runnable = invocation.getArgument(0);
runnable.run();
return serverContext;
});
// 2nd lambda
Receiver receiver = Mockito.mock(Receiver.class);
Mockito.doAnswer((Answer<Void>) invocation -> {
FullBytesCallback callback = invocation.getArgument(0);
callback.handle(serverContext, message);
return null;
}).when(receiver).receiveFullBytes(Mockito.any(FullBytesCallback.class));
Mockito.when(serverContext.getRequestReceiver()).thenReturn(receiver);
// class under test - method invocation
handler.handleRequest(serverContext);
// buisness logic call verification
ArgumentCaptor<HttpServerExchange> captor1 = ArgumentCaptor.forClass(HttpServerExchange.class);
ArgumentCaptor<byte[]> captor2 = ArgumentCaptor.forClass(byte[].class);
Mockito.verify(logic).someMethod(captor1.capture(), captor2.capture());
Assertions.assertEquals(serverContext, captor1.getValue());
Assertions.assertEquals(message, captor2.getValue());
}
}
As others already mentioned you should only use that approach for legacy code.
A simple refactoring could just push the entire part you need to test into its own method, which - in the example above - would just be the buisness logic itself.
There is no explicit need to test the undertow framework yourself.

Related

Spock -Unit Test:How to write spock unit test for #around annotation which takes Mono

Hi I am using following code to print logs using aop in my webflux app,I have trouble writing unit/integration tests ?can we verify log interactions here?Any help would be appreciated
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {}
#Aspect
#Slf4j
public class LoggerAspect {
#Around("#annotation(Loggable)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
var result = joinPoint.proceed();
if (result instanceof Mono) {
var monoResult = (Mono) result;
AtomicReference<String> traceId = new AtomicReference<>("");
return monoResult
.doOnSuccess(o -> {
var response = "";
if (Objects.nonNull(o)) {
response = o.toString();
}
log.info("Enter: {}.{}() with argument[s] = {}",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs());
log.info("Exit: {}.{}() had arguments = {}, with result = {}, Execution time = {} ms",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs()[0],
response, (System.currentTimeMillis() - start));
});
}
}
}
Test Failing.
Somehow when i debug pointer is not going inside doOnNext Method.And I am not sure how I can assert log interaction in above Logging aspect.In Junit5 I know I can use mockito for each method and return something ,but how i can rerturn in spock.
class LogAspectTest extends Specification {
private static final String MOCK_METHOD_LOG_VALUE = "mockMethodLogValue"
private Logger log = Mock()
private ProceedingJoinPoint mockJoinPoint = Mock()
private static Mono<String> methodReturn = Mono.just(["Data", "Data"])
private LogAspect logAspect = new LogAspect(log)
#Unroll
def 'logAround verify log interaction'() {
given:
mockJoinPoint.proceed() == Mono.just("Hello")
final Method method = TestClass.class.getMethod("mockMethod")
when:
logAspect.logAround(mockJoinPoint)
then:
interaction { mockJoinPointAndMethodSignatureInteractions(method, methodReturnToUse) }
where:
resultType | methodReturnToUse
'Mono' | methodReturn
}
private void mockJoinPointAndMethodSignatureInteractions(Method method, Publisher result) {
1 * mockJoinPoint.proceed() >> result
1 * log.info() >> ""
}
private static class TestClass {
#Loggable
Mono<String> mockMethod() { return Mono.just("data") }
}
}
Is it recommended to write Integration Test for #Loggable annotation since it just logging not sure how can write Integration Test which assert the log statements
Like I said in my comment, you cannot easily mock a private static final field without using add-on tools like PowerMock or similar. I think that whenever you need something like that, you should rather refactor your code for better testability. Here is an idea which is far from perfect, but I want to give you an idea about how you could unit-test your aspect. As for an integration test, you can also do that, but ask yourself what you want to test: really the aspect or that Spring AOP pointcut matching works correctly?
Anyway, let us assume that your classes under test are:
package de.scrum_master.stackoverflow.q64164101;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {}
package de.scrum_master.stackoverflow.q64164101;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import java.util.Objects;
import java.util.function.Consumer;
#Aspect
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class.getName());
#Around("#annotation(Loggable)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
if (result instanceof Mono)
return ((Mono) result).doOnSuccess(getConsumer(joinPoint, start));
return result;
}
public Consumer getConsumer(ProceedingJoinPoint joinPoint, long start) {
return o -> {
String response = "";
if (Objects.nonNull(o))
response = o.toString();
log.info("Enter: {}.{}() with argument[s] = {}",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs());
log.info("Exit: {}.{}() had arguments = {}, with result = {}, Execution time = {} ms",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs()[0],
response, (System.currentTimeMillis() - start));
};
}
}
See how I factored out the lambda into a helper method? It has two effects:
It makes the logAround(ProceedingJoinPoint) advice method more readable.
It permits you to stub the helper method and instead of verifying that logging is done you just verify that the helper method was called for Mono results (and not called for other result types).
The test in its simplest form could look like this:
package de.scrum_master.stackoverflow.q64164101
import org.aspectj.lang.ProceedingJoinPoint
import reactor.core.publisher.Mono
import spock.lang.Specification
class LogAspectTest extends Specification {
LogAspect logAspect = Spy()
ProceedingJoinPoint joinPoint = Mock()
def "aspect target method returns a Mono"() {
given:
joinPoint.proceed() >> Mono.just("Hello")
when:
logAspect.logAround(joinPoint)
then:
1 * logAspect.getConsumer(joinPoint, _)
}
def "aspect target method does not return a Mono"() {
given:
joinPoint.proceed() >> "dummy"
when:
logAspect.logAround(joinPoint)
then:
0 * logAspect.getConsumer(joinPoint, _)
}
}
Please note how I use a Spy (i.e. a partial mock based on the original object) in order to selectively stub the helper method.
Update: An alternative for more integrative testing would be to configure your logging framework to log into a target which you can control and verify, e.g. log into an in-memory database or into a buffer which you can access.

How would you test static method URLEncoder.encode?

I have this below method.
protected static String encode(String url) {
try {
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOGGER.warn("exception occured while encoding url {}", url);
}
return url;
}
I am unable to provide a junit test for this because I can't mock URLEncoder. There are 2 possible outcomes of this method
encoded url
original url if there is some exceptions
I am able to create a test method for first outcome. how would you create test method for the second outcome?
The fundamental theorem of software engineering (FTSE) is a term originated by Andrew Koenig to describe a remark by Butler Lampson attributed to the late David J. Wheeler:
"We can solve any problem by introducing an extra level of indirection."
[...]
The theorem is often expanded by the humorous clause "…except for the problem of too many levels of indirection," referring to the fact that too many abstractions may create intrinsic complexity issues of their own. (Source: Wikipedia)
So let's say there's a class that has a static method named encode:
public final class UrlHelper {
protected static String encode(String url) {
try {
url = URLEncoder.encode(url, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOGGER.warn("exception occured while encoding url {}", url);
}
return url;
}
}
and your code depends on it:
public class MyClass {
public void doSomething(String someUrl) {
// ...
String encodedUrl = UrlHelper.encode(someUrl);
// ...
}
}
and you want to test MyClass.doSomething(String someUrl) but you want to mock UrlHelper.encode(someUrl). One option is to define another class such as
public final class MyUrlHelper {
protected String encode(String url) {
return UrlHelper.encode(someUrl);
}
}
As MyUrlHelper.encode(String url) is not static, you can refactor your original code and test it by relying on dependency injection and mocking the non-static MyUrlHelper.encode(String url):
// Refactored
public class MyClass {
private MyUrlHelper myUrlHelper;
public UrlHelper(MyUrlHelper muUrlHelper) {
this.myUrlHelper = myUrlHelper;
}
public void doSomething(String someUrl) {
// ...
String encodedUrl = myUrlHelper.encode(someUrl);
// ...
}
}
// Test
#Test
public void myTest() {
// setup myUrlHelper and configure it
MyUrlHelper myUrlHelper = mock(MyUrlHelper.class);
when(myUrlHelper.encode(...)).thenReturn(...);
// inject
MyClass myObject = new MyClass(myUrlHelper);
// stimulate
myObject.doSomething("...")
}
Another option is to use Mockito using the PowerMockRunner as explained by #Marimuthu Madasamy.
However, I don't see any benefit in mocking UrlHelper.encode or URLEncoder.encode here. It is not an external system (a database, a file system, a message broker, a SOAP API, a REST API, etc.) so I don't see any gains by mocking it.
You could use PowerMockito to mock static methods. Assuming the static method in your post is in a class called HelloWorld, here are the two tests where the first test is testing the positive case and the second test
is testing the exception case:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.powermock.api.mockito.PowerMockito.doThrow;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(PowerMockRunner.class)
#PrepareForTest(HelloWorld.class)
public class HelloWorldTest {
#Test
public void encode_returnsEncoded() throws UnsupportedEncodingException {
// given
mockStatic(URLEncoder.class);
when(URLEncoder.encode(any(String.class), any(String.class)))
.thenReturn("testUrlEncoded");
// when
String encoded = HelloWorld.encode("testUrl");
// then
assertThat(encoded).isEqualTo("testUrlEncoded");
}
#Test
public void encode_returnsInputOnException() {
// given
mockStatic(URLEncoder.class);
doThrow(new Exception("exception from test"))
.when(URLEncoder.class);
// when
String encoded = HelloWorld.encode("testUrl");
// then
assertThat(encoded).isEqualTo("testUrl");
}
}
If you are willing to use Lombok, I got a practical approach for you:
#lombok.Generated // Function won't raise up in Jacoco coverage report, see https://stackoverflow.com/a/56327700/1645517
#lombok.SneakyThrows(UnsupportedEncodingException.class) // Suppress handling this exception, see https://projectlombok.org/features/SneakyThrows
private static String urlEncode(final String valueToEncode) {
return URLEncoder.encode(valueToEncode, StandardCharsets.UTF_8.name());
}
Jacoco will ignore this method
The UnsupportedEncodingException cannot be thrown here. Let Lombok handle this ugly circumstance.

piTest removed call to "forEach" SURVIVED

TL;DR I think mutation should be killed but it survived. I am looking for the reason why it's happening and how to fix these 2 things: (1) Timeout (2) mutation survived.
Details I have a spring web application and am testing using testng. I have extracted the relevant part of the code. Please excuse me if I have introduced any problem while extracting the code for this question. I have a passing test case which verifies that the callFunction is called 8 times. This is verified using verify(a, atLeast(8)).called(); After seeing the piTest report it seems that if the callFunction is removed the function will still have a.called(); 8 times ... which is unexpected.
I have checked by removing callFunction from the source and the test case does fail. See the section Modified1 Rat.java.
Also I have checked by removing the forEach and the test case does fail. See Modified2 Rat.java.
There is an even interesting thing that when I changed only the (formatting) location of the text in Rat.java like shown in Modified3 Rat.java section the piTest report changed.
Type.java
package lab.rat;
public class Type {
}
Action.java
package lab.rat;
import org.springframework.stereotype.Component;
#Component public class Action {
public void called() {}
}
Rat.java
package lab.rat;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component public class Rat {
#Autowired private Action a;
public void testee() {
Map<Type, Integer> properties = new HashMap<>();
IntStream
.range(0, 8)
.forEach(index -> properties.put(new Type(), index));
properties
.entrySet()
.stream()
NOTICE FOLLOWING LINE
.forEach(entry -> callFunction()); // removed call to lab/rat/Rat::callFunction ? TIMED_OUT
// removed call to java/util/stream/Stream::forEach ? SURVIVED
}
private void callFunction() {
a.called();
}
}
RatTest.java
package lab.rat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import lab.rat.config.SpringConfigurationForTest;
public class RatTest extends SpringConfigurationForTest {
#InjectMocks Rat rat;
#Mock Action a;
#BeforeMethod public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test public void testTestee() {
rat.testee();
verify(a, atLeast(8)).called();
}
}
Modified1 Rat.java -- test fails
.stream()
.forEach(entry -> {});
Modified2 Rat.java -- test fails
.stream();
Modified3 Rat.java -- one more mutation created
.stream()
.forEach( // removed call to java/util/stream/Stream::forEach ? SURVIVED
// removed call to lab/rat/Rat::callFunction ? TIMED_OUT
entry -> callFunction() // replaced return of integer sized value with (x == 0 ? 1 : 0) ? KILLED
);
Years later but no one seemed to mention that (Spring) #Component and (Mockito) #InjectMocks are mutually exclusive solutions. You have multiple generated subclasses of Rat in play so PIT is simply confused about what's going on. It probably mutated the wrong one.

SOAP with MockitoJUnit is not working in java

#WebService(name = "ManageSearchDataPortType", targetNamespace = "https://abc/searchData", serviceName = "ManageSearchData", portName = "ManageSearchDataPort", wsdlLocation = "/WEB-INF/wsdl/ManageSearchData.wsdl")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
import com.xyz.SearchDataRequest;
import com.xyz.SearchDataResponse;
#XmlSeeAlso({ ObjectFactory.class, com.abc.ObjectFactory.class, com.abc..ObjectFactory.class, com.abc..ObjectFactory.class })
public class ManageSearchDataService {
#WebResult(name = "searchDataResponse", partName = "searchDataResponse", targetNamespace = "https://abc/searchData")
#WebMethod(action = "http://example.com/wsdl/ManageABC/2008/10/22#searchData")
public SearchDataResponse searchData(#WebParam(name = "searchDataRequest", partName = "searchDataRequest", targetNamespace = "https://abc/searchData")
SearchDataRequest searchDataRequest) {
System.out.println(" searchData Methodthod");
}
public void test();
{
System.out.println("TEST Method");
}
}
Below is my junit mockito test class contains the test method i.e doSomething. Here i'm trying to mack ManageSearchDataService class which shown above contains searchData and test method, But
searchData not being called by mock object
and normal test method has been called successfully.
searchData is my WebMethod as you can see the method.
HeaderClass is just normal call which contains getMPLNHeader static method.
Please anyone have any idea how to do this let me know.
package com.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.xyz.SearchDataRequest;
import com.aaa.ManageSearchDataService;
#RunWith(MockitoJUnitRunner.class)
public class JunitTestClass {
#Mock ManageSearchDataService mockObj ;
#Test
public void doSomething() {
SearchDataRequest searchDataRequest= new SearchDataRequest();
searchDataRequest.setStandardHeader(HeaderClass.getMPLNHeader("ASV"));
mockObj.searchData(searchDataRequest); // not Working
mockObj.test(); // this working fine printing data
}
}
You should not mock the class you want to test. You mock all other classes/objects except the class you want to test.
As people already pointed out, mocking ManageSearchDataService doesn't make any sense. You are creating a mock object of your service and then test that mock, this isn't telling you anything about your service. Mocking is for dependencies, for example, if your service used another object to load data from a database, then mocking this would be a great idea for a unit test (because otherwise, your test would fail if the DB is down - even if the code it completely ok).
Mocking allows you to test one single unit by "simulating" all of its dependencies, so you don't have to rely on them being ok (that's the problem of other unit tests), because you simulate it in a way as if they were ok (or not, if you want to test error handling in your unit).
Since your class does not contain any dependencies like this at all (it doesn't actually do anything besides printing) there is nothing you could or should mock here.

how to mock a class used in the code

public void sendMail() {
/* I have simplified the method for here */
Transport t;
t.send();
}
When I am writing unit test for this, since I don't want to actually send the mail, I want to mock the Transport class. Is it possible and if so, how to do it ?
'cglib' may fits.
Use ‘Enhancer' to proxy the 'Transport' class. In order not to actually sending the mail, you need pass into the 'Enhancer' a 'MethodInterceptor' which will not invoke the 'send()' method in super class.
Unless you really want to use mockito, you can quite easily handcraft your own test double.
What you can do is create an interface that knows how to send mail:
public interface TransportInterface {
public void send(Message msg);
}
Let the mail sending class use this interface to send the mail:
public class MailSender {
private TransportInterface transport;
public MailSender(TransportInterface transport) {
this.transport = transport;
}
public void sendMail(Message msg) {
/* This is the method from your question */
this.transport.send(msg);
}
}
In production you use an implemenation of TransportInterface that actually send the mail:
public class TransportAdapter implements TransportInterface {
private Transport transport; // Created elsewhere...
public void sendMail(Message msg) {
transport.send(msg);
}
}
And in your test code you can use a fake:
public class TransportFake implements TransportInterface {
public void sendMail(Message msg) {
// I don't send the mail!
}
}
(It's been a while since I coded java. Hope there are not too many errors. Also, you can probably do a better job naming the classes than I have.)
You can try to use Mockito library:
here is example code:
import org.mockito.Mock;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class YourTestClass{
#Mock
Transport t;
#Test
public void someTest(){
t = Mockito.mock(Transport .class);
Mockito.when(t.send()).thenReturn(true);
}
}
Here is a solution which works regardless of how the Transport t object is obtained:
#Test
public sendMail(
// Mocks all current and future Transport instances,
// including those belonging to subclasses (if any).
#Capturing final Transport t)
{
new SUT().sendMail();
// Verifies that the t.send() method was called:
new Verifications() {{ t.send(); }};
}
The mocking API used above is JMockit (which I develop).

Categories