How to use camel mock to get full XML? - unit-testing

In a unit test with camel, I can make asserts using xpath to check if the output xml is correct. But instead, I'd like to use XMLUnit to validate the xml against another entire xml file. Is that possible? The following test succeeds, but I'd like to adjust it to get the actual XML.
#Test
public void testSupplierSwitch() throws Exception
{
MockEndpoint mock = getMockEndpoint("mock:market-out");
mock.expectedMessageCount(1);
EdielBean edielBean = (EdielBean)context.getBean("edielbean");
edielBean.startSupplierSwitch(createCustomer(), createOrder(), "54", "43");
assertMockEndpointsSatisfied();
}

Here is one example of how you can solve it, using mockEndpoint.getExchanges()
public class XmlUnitTest extends CamelTestSupport{
#EndpointInject(uri = "mock:market-out")
MockEndpoint marketOut;
#Override
#Before
public void setUp() throws Exception {
super.setUp();
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:in")
.setBody(constant("<xml>data</xml>"))
.to(marketOut.getEndpointUri());
}
});
}
#Test
public void sameXml() throws Exception {
marketOut.expectedMessageCount(1);
template.sendBody("direct:in", "body");
marketOut.assertIsSatisfied();
final String actual = marketOut.getExchanges().get(0).getIn().getBody(String.class);
final Diff diff = XMLUnit.compareXML("<xml>data</xml>", actual);
assertTrue(diff.similar());
assertTrue(diff.identical());
}
#Test()
public void notSameXml() throws Exception {
marketOut.expectedMessageCount(1);
template.sendBody("direct:in", "body");
marketOut.assertIsSatisfied();
final String actual = marketOut.getExchanges().get(0).getIn().getBody(String.class);
final Diff diff = XMLUnit.compareXML("<xml>invalid</xml>", actual);
assertFalse(diff.similar());
assertFalse(diff.identical());
}
}

Related

MapReduce - Mock with Mokito

I have the a reducer class that I wanted to write test cases:
Reduce class:
public class MyReducer extends Reducer<Text, Text, NullWritable, Text> {
private static final Logger LOG = LogManager.getLogger(MyReducer.class);
public static List<String> l1 = new ArrayList<String>();
String id = null;
private MultipleOutputs<NullWritable, Text> mos;
#Override
public void setup(final Context context) throws IOException, InterruptedException {
mos = new MultipleOutputs<NullWritable, Text>(context);
final Path[] uris = DistributedCache.getLocalCacheFiles(context.getConfiguration());
try {
final BufferedReader readBuffer1 = new BufferedReader(new FileReader(uris[0].toString()));
String line;
while ((line = readBuffer1.readLine()) != null) {
l1.add(line);
}
readBuffer1.close();
} catch (Exception e) {
LOG.error(e);
}
}
public void reduce(final Text key, final Iterable<Text> values, final Context context)
throws IOException, InterruptedException {
final String[] key1 = key.toString().split("-");
final String keyA = key1[10];
final String date = key1[1];
/* Some condition check */
mos.write(NullWritable.get(), new Text(inputEventValue), keyA + "//date=" +
date.substring(0, 4) + "-" + date.substring(4, 6));
}
#Override
public void cleanup(final Context context) throws IOException, InterruptedException {
mos.close();
}
}
Test Case looks like :
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#Mock
private MyReducer.Context mockContext;
MyReducer reducer;
MultipleOutputs<NullWritable, Text> mos;
#Before
public void setUp() {
reducer = new MyReducer();
}
#Test
public void myReducerTest() throws Exception {
MyReducer spy = PowerMockito.spy(new MyReducer());
doNothing().when(spy).setup(mockContext);
mos = new MultipleOutputs<NullWritable, Text>(mockContext);
List<Text> sline = new ArrayList<>() ;
List<String> l1 = new ArrayList<String>();
l1.add(“1234”);
sline.add(new Text(“xyz”));
Whitebox.setInternalState(MyReducer.class,”l1", l1);
Whitebox.setInternalState(MyReducer.class,"mos",mos);
reducer.reduce(new Text(“xyz-20200101-1234),sline,mockContext);
}
#After
public void tearDown() throws Exception {
/*
* this will do the clean up part
*/
verifyNoMoreInteractions(mockContext);
}
When running in Debug mode it goes to the reducer's reduce method and fails with NullPointerException where mos write statement is?
Complete Stack trace:
java.lang.NullPointerException
at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.getNamedOutputsList(MultipleOutputs.java:196)
at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.<init>(MultipleOutputs.java:324)
at MyTest.myeducerTest
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:118)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
Mocking mos errors as mos is not a static.
Any suggestion.
Junit - ReduceDriver, withInput, withOutput,testRun doesn't work.
Thanks.
I tried mocking Multiple outputs as suggested:
import org.apache.hadoop.mapreduce.lib.output.MultipleOutputs;
#Mock
private MyReducer.Context mockContext;
List<String> namedOut = new ArrayList<>();
namedOut.add("NM1");
namedOut.add("NM2");
MultipleOutputs spy = PowerMockito.spy(new MultipleOutputs<>(mockContext));
when(spy, "getNamedOutputsList(mockContext)").thenReturn(namedOut);
But this gives me error : org.powermock.reflect.exceptions.MethodNotFoundException: no method found with name 'getNamedOutputsList(() anyObject())' with parameter types : [] in class org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.
Looks like you did not define what mockContext.getContext() should return for your test, so it returns null and fails.
Based on this sourcecode the methods looks like this (so you might use a different version):
private static List<String> getNamedOutputsList(JobContext job) {
List<String> names = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(
job.getConfiguration().get(MULTIPLE_OUTPUTS, ""), " ");
while (st.hasMoreTokens()) {
names.add(st.nextToken());
}
return names;
}
JobContext seems to refer to your mock Reducer.Context mockContext, so you need to define the appropriate behaviour so that it returns what it is supposed to return.
Note that this call originates from the constructor of MultipleOutputs.
Also take note of the static getCountersEnabled method that is invoked from the constructor and interacts with the context.
Mocking mos errors as mos is not a static.
You could probably use reflections to put a mocked version of mos into your MyReducer class.
Check here for some example on how to mock a private static field.
Edit:
If you try to mock the conig do it like this:
Configuration config = Mockito.mock(Configuration.class);
when(mockContext.getConfiguration()).thenReturn(config);
As far as I see the get that are invoked on the configuration object always provide a default value, so it shouldn't matter if the key/value pair is in there or not.

Unit Test Vertx AsyncHandler with Mockito

I am trying yo unit test my method which internally calls DB which I am trying to mock and want to return the response but getting an error.
public void processAlert(JsonObject requestInput, Handler<AsyncResult<JsonObject>> handler) {
JsonObject jobInput = new JsonObject().put("requestInput", requestInput);
dbService.saveJobDetails(jobInput, readyHandler -> {
if (readyHandler.succeeded()) {
handler.handle(Future.succeededFuture(readyHandler.result()));
} else {
handler.handle(Future.failedFuture(readyHandler.cause()));
}
});
}
Test code for mocking the dbService
#Mock
DBService dbService;
#Captor
private ArgumentCaptor<Handler<AsyncResult<JsonObject>>> resultHandlerCaptor;
#Test
public void test() {
AsyncResult<JsonObject> result = Future.succeededFuture(new JsonObject().put("status", "success"));
Mockito.verify(dbService).saveJobDetails(Mockito.any(JsonObject.class), resultHandlerCaptor.capture());
Handler<AsyncResult<JsonObject>> handler = resultHandlerCaptor.getValue();
handler.handle(result);
But when I am running this test getting an Exception
Wanted but not invoked:
dbService.saveJobDetails(
<any io.vertx.core.json.JsonObject>,
<Capturing argument>
);
Actually, there were zero interactions with this mock.

Spring-Webflux: Handler function unit test is throwing UnsupportedMediaTypeStatusException

I am trying to write Unit test to the handler function, I followed the example from the Spring project. Can someone help me why the following test is throwing UnsupportedMediaTypeStatusException?
Thanks
Handler function
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
log.info("{} Processing create request", serverRequest.exchange().getLogPrefix());
return ok().body(serverRequest.bodyToMono(Person.class).map(p -> p.toBuilder().id(UUID.randomUUID().toString()).build()), Person.class);
}
Test Class
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyHandlerTest {
#Autowired
private MyHandler myHandler;
private ServerResponse.Context context;
#Before
public void createContext() {
HandlerStrategies strategies = HandlerStrategies.withDefaults();
context = new ServerResponse.Context() {
#Override
public List<HttpMessageWriter<?>> messageWriters() {
return strategies.messageWriters();
}
#Override
public List<ViewResolver> viewResolvers() {
return strategies.viewResolvers();
}
};
}
#Test
public void handle() {
Gson gson = new Gson();
MockServerWebExchange exchange = MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create")
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
MockServerHttpResponse mockResponse = exchange.getResponse();
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<ServerResponse> serverResponseMono = myHandler.handle(serverRequest);
Mono<Void> voidMono = serverResponseMono.flatMap(response -> {
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
boolean condition = response instanceof EntityResponse;
assertThat(condition).isTrue();
return response.writeTo(exchange, context);
});
StepVerifier.create(voidMono)
.expectComplete().verify();
StepVerifier.create(mockResponse.getBody())
.consumeNextWith(a -> System.out.println(a))
.expectComplete().verify();
assertThat(mockResponse.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
}
}
Error Message:
java.lang.AssertionError: expectation "expectComplete" failed (expected: onComplete(); actual: onError(org.springframework.web.server.UnsupportedMediaTypeStatusException: 415 UNSUPPORTED_MEDIA_TYPE "Content type 'application/octet-stream' not supported for bodyType=com.example.demo.Person"))
I found that I missed .contentType(MediaType.APPLICATION_JSON) to my mock request.
MockServerWebExchange.from(
MockServerHttpRequest.post("/api/create").contentType(MediaType.APPLICATION_JSON)
.body(gson.toJson(Person.builder().firstName("Jon").lastName("Doe").build())));
fixed my issue.

Unit test verify method was called inside RxJava's doOnSuccess operator

I have the following code I'm trying to unit test :
if (networkUtils.isOnline()) {
return remoteDataSource.postComment(postId, commentText)
.doOnSuccess(postCommentResponse ->
localDataSource.postComment(postId, commentText))
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.mainThread());
} else {
return Single.error(new IOException());
}
And this is how I'm trying to test it :
#Test
public void postComment_whenIsOnline_shouldCallLocalToPostComment() throws Exception {
// Given
when(networkUtils.isOnline())
.thenReturn(true);
String postId = "100";
String comment = "comment";
Response<PostCommentResponse> response = postCommentResponse();
when(remoteDataSource.postComment(anyString(), anyString()))
.thenReturn(Single.just(response));
// When
repository.postComment(postId, comment);
// Then
verify(localDataSource).postComment(postId, comment);
}
where I fake Response from Retrofit like :
private Response<PostCommentResponse> postCommentResponse() {
PostCommentResponse response = new PostCommentResponse();
response.setError("0");
response.setComment(postCommentResponseNestedItem);
return Response.success(response);
}
but it results to : Actually, there were zero interactions with this mock.
Any ideas ?
EDIT :
#RunWith(MockitoJUnitRunner.class)
public class CommentsRepositoryTest {
#Mock
private CommentsLocalDataSource localDataSource;
#Mock
private CommentsRemoteDataSource remoteDataSource;
#Mock
private NetworkUtils networkUtils;
#Mock
private PostCommentResponseNestedItem postCommentResponseNestedItem;
private CommentsRepository repository;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
BaseSchedulerProvider schedulerProvider = new ImmediateSchedulerProvider();
repository = new CommentsRepository(localDataSource, remoteDataSource, networkUtils, schedulerProvider);
}
// tests
}
When you want to test an Observable you have to subscribe to it so it will start emitting items.
As soon as I used :
TestObserver<Response<PostCommentResponse>> testObserver = new TestObserver<>();
and subscribed to :
repository.postComment(postId, comment)
.subscribe(testObserver);
the test worked as expected.

Apache Camel - testing log message

I'm trying to test a Camel route which uses from(x).to(y).log("SuccessKey123") and onException(HttpOperationFailedException.class).log("ErrorKey123").
How can I test that Camel logs "SuccessKey123" when the message was successfully processed or it logs "ErrorKey123" if HttpOperationFailedException is thrown?
My RouteBuilder():
#Component
public class myHttp4RouteBuilder extends SpringRouteBuilder {
public static final ID = "foo";
#Override
public void configure() throws Exception {
onException(HttpOperationFailedException.class)
.log("ErrorKey123")
.to(ERROR_QUEUE)
.handled(true);
from(AWS_SQS_ENDPOINT)
.to(REST_API_ENDPOINT)
.log("SuccessKey123");
}
}
Testclass:
public class myHttp4RouteBuilderTest {
#Produce(uri = MOCK_ROUTE_FROM)
protected ProducerTemplate template;
#EndpointInject(uri = MOCK_ROUTE_TO)
private MockEndpoint mockEndpoint;
#Autowired
private CamelContext camelContext;
#Before
public void setup() throws Exception{
RouteDefinition rd = camelContext.getRouteDefinition(myHttp4RouteBuilder.ID);
rd.adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith(MOCK_ROUTE_FROM);
weaveByToUri(ERROR_QUEUE)
.replace()
.to(MOCK_ROUTE_TO);
}
});
}
#Test
#DirtiesContext
public void testSuccess() throws Exception {
// throw an HttpOperationFailedException
mockEndpoint.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
throw new HttpOperationFailedException("Exception", 400, null, null, null, null);
}
});
//
//
// How can I test here that camel logs "ErrorKey123"
//
//
template.sendBody(MOCK_ROUTE_FROM, "some content");
mockEndpoint.assertIsSatisfied();
}
}
Thank you very much!
Camel uses slf4j so you can just add some test appender on setup to the required logger and check what was logged after that (or even mock appender interface)
I got it ;-) You put me to the right way. Thanks!
This is my solution:
First: create a custom Appender
package de.example.test;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.slf4j.event.LoggingEvent;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Plugin(name="myAppenderForTesting", category="Core", elementType="appender", printObject=true)
public class MyAppenderForTesting extends AbstractAppender {
/** Here we collect all log messages */
public static List<LogEvent> logEvents = new ArrayList<>();
protected MyAppenderForTesting(String name, Filter filter, Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
#PluginFactory
public static MyAppenderForTesting createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
return new MyAppenderForTesting(name, filter, layout, true);
}
#Override
public void append(LogEvent event) {
try {
logEvents.add(event);
} catch (Exception ex) {
if (!ignoreExceptions()) {
throw new AppenderLoggingException(ex);
}
} finally {
}
}
/**
* Clear log messages
*/
public static void clean() {
logEvents.clear();
}
}
Short explanation: with append() method we add each log event to a public static variable logEvents. Later in test we can access logEvents.
It was a little bit difficult to get this appender working with log4j. In my case I created a log4j2.xml in the test resources src\test\resources\log4j2.xml.
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration packages="de.example">
<Appenders>
<myAppenderForTesting name="myAppenderForTesting">
<PatternLayout alwaysWriteExceptions="false" pattern="%d{dd.MM.yyyy HH:mm:ss} %-5p %t [%C{1}.%M:%L] %m %ex{10000}%n" />
</myAppenderForTesting>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="myAppenderForTesting"/>
</Root>
</Loggers>
</Configuration>
In my test classes I can access directly to MyAppenderForTesting.logEvents. For example
for (LogEvent event : MyAppenderForTesting.logEvents) {
String message = event.getMessage().toString();
if (message.contains(search)) {
// do somethind
}
}
A different approach could be to use a log listener to collect the messages and assert them afterwards:
// adding log listener
Set<String> logMessages = new HashSet<>();
((SpringBootCamelContext) camelContext)
.addLogListener((Exchange exchange, CamelLogger camelLogger, String message) -> {
logMessages.add(message);
return message;
});
// others test initializations...
// asserting the expected log message
assertThat(logMessages.stream()
.filter(m -> m.contains("looking for this message")).count()).isEqualTo(1);
You can also use Camel's advice-with and then mock/replace those log endpoints with a mock etc, and then just assert that Camel routed a message to those depending on what you do.
http://camel.apache.org/advicewith.html
I agree with Claus Ibsen's answer. You could use AdviceWith and weaveByType(LogDefinition.class).selectIndex(...) to pinpoint the logging you expect.
Old thread but it has a high visibility, so I hope this answer helps someone.
e.g.
#SpringBootTest
#CamelSpringBootTest
public class MyRouteTest {
#Autowired
protected CamelContext context;
#EndpointInject("mock:successRoute")
private MockEndpoint successRouteMockEndpoint;
#EndpointInject("mock:failRoute")
private MockEndpoint failRouteMockEndpoint;
...
#Test
public void Given_SuccessfulCall_ThenLogSuccess() throws Exception {
AdviceWith.adviceWith(context, myRouteId,
a -> a.weaveByType(LogDefinition.class).selectIndex(1).replace().to(successRouteMockEndpoint));
// directives to mock a successful response
successRouteMockEndpoint.expectedMessageCount(1);
failRouteMockEndpoint.expectedMessageCount(0);
// trigger route
successRouteMockEndpoint.assertIsSatisfied();
failRouteMockEndpoint.assertIsSatisfied();
}
#Test
public void Given_UnsuccessfulCall_ThenLogFailure() throws Exception {
AdviceWith.adviceWith(context, myRouteId,
a -> a.weaveByType(LogDefinition.class).selectIndex(0).replace().to(failRouteMockEndpoint));
// directives to mock an unsuccessful response
successRouteMockEndpoint.expectedMessageCount(0);
failRouteMockEndpoint.expectedMessageCount(1);
// trigger route
successRouteMockEndpoint.assertIsSatisfied();
failRouteMockEndpoint.assertIsSatisfied();
}
}