Camel adviceWith for ExceptionHandler - java

I need to test my Camel Exception handler:
#Configuration
public class RouteConfiguration extends RouteBuilder {
#Override
public void configure() throws Exception {
onException(HttpOperationFailedException.class).
handled(true).
log("HttpOperationFailedException: ${exception}").
onExceptionOccurred(myRestExceptionProcessor).id("myRestExceptionProcessor").end();
from("direct:myPrettyRoute").routeId("myPrettyRoute");//lots of routing here
}
}
I'm trying to add adviceWith after myRestExceptionProcessor, but can't find a way.
public class MyExceptionRoutingTest {
#Autowired
private CamelContext context;
#Before
public void before() throws Exception {
if (ServiceStatus.Stopped.equals(context.getStatus())) {
log.info("prepare mocks endpoint");
List<OnExceptionDefinition> ed = context.getErrorHandlerBuilder().getErrorHandlers(context.getRoutes().get(0).getRouteContext());
//FAILS, because context.getRoutes() is empty at the moment
//even if it wasn't, getErrorHandlerBuilder() is deprecated
}
}
}
I need to add something like this for the exceptionHandler definition:
.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myExceptionProcessor").after().to(myResultEndpoint).id("myResponseEndpoint");
}
});
Is it possible?

I don't fully understand if you want to test your error handler (onException block) or just your myRestExceptionProcessor, but from a Camel perspective these are two kinds of tests:
Routing-Tests to test your routing logic and make sure that messages are correctly routed under various conditions that could happen in the route. This is the kind of tests you write with the Camel Testkit (that offers adviceWith and much more).
Classic unit tests to test an isolated Bean, Processor or anything else that is used in the route to implement business logic. This kind of test is done with JUnit, TestNG or other classic unit test frameworks, it has nothing to do with Camel. Do not try to test such components with Camel Route tests since it is much more complicated than in a unit test!
So, if you want to test your routing when an error occurs you throw the needed error in your route test to trigger the error handler. If you use a dependency injection framework like Spring this is easy since you can inject a test Bean that throws an error instead of a real Bean used in the route.
To add a Mock endpoint at the end of a route, use adviceWith
.adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveAddLast().to("mock:error");
}
}
Hope this helps a bit. Feel free to extend your question to elaborate your problem a bit more.

I've solved the trick as follows, without changing the route:
//entry point of the route is invoked here
Exchange send = myProducer.withBody("body is here").send();
HttpOperationFailedException exception = send.getException(HttpOperationFailedException.class);
String responseBody = exception.getResponseBody();
//recieved result and made assertions
assert responseBody != null; // any other assertions

Related

Testing camel routes

I have multiple routes classes defined in my project under com.comp.myapp.routes.
For testing these I am mocking the end route and checking/comparing delivery received.
Say for example I have below routes:
public class MyRoute1 extends RouteBuilder {
public void configure() throws Exception {
//Route_1 code
}
}
public class MyRoute2 extends RouteBuilder {
public void configure() throws Exception {
//Route_2 code
}
}
....
...//some route impl
..
public class MyRouteN extends RouteBuilder {
public void configure() throws Exception {
//Route_N code
}
}
Now for all these routes the test case that I wrote seems same.
First mock it.
Mock for MyRoute1:
public class MyRoute1_Mock extends RouteBuilder {
public void configure() throws Exception {
from("direct:sampleInput")
.log("Received Message is ${body} and Headers are ${headers}")
.to("mock:output");
}
}
Test for MyRoute1:
public class MyRoute1_Test extends CamelTestSupport {
#Override
public RoutesBuilder createRouteBuilder() throws Exception {
return new MyRoute1_Mock();
}
#Test
public void sampleMockTest() throws InterruptedException {
String expected="Hello";
/**
* Producer Template.
*/
MockEndpoint mock = getMockEndpoint("mock:output");
mock.expectedBodiesReceived(expected);
String input="Hello";
template.sendBody("direct:sampleInput",input );
assertMockEndpointsSatisfied();
}
}
Now to make unit test for other classes just copy and paste the above code with different name say MyRoute2_Test , MyRoute3_Test , ...MyRouteN_Test.
So what did it actually tested?
It's just written for the purpose of writing test case.
It actually just checks/tests if mock library and camel-test library work or not Not our code works or not?
How should it actually be done?
You want to test your Camel routes but in the test you mock them away. So yes, you are testing your route mock instead of the real route.
To test your real routes:
Send a message to your real routes from endpoint
If this is not easy, mock your from endpoint (not the entire route!) by replacing it with a direct endpoint. This is quite easy with adviceWith
The test message is going through your route
Assert that any to endpoint receives the correct message by mocking these endpoints too. Again, use adviceWith for that. And Camel Mock of course
You can get the received messages (Exchanges) from a Camel Mock to do in depth assertions
If you got the happy test, start to write negative tests by injecting errors in your route. adviceWith can help here too
... and so on
If you are completely new to Camel route tests, get Camel in Action 2nd edition. It explains all mentioned testing aspects for Camel applications on 65 pages. And of course it also takes you on a complete ride through the Camel universe on much more pages.
By the way: if testing your routes is hard, they are too complex. Start to divide your routes so that they are easily testable.
The route you show doesn't really do anything to the messages traversing it, so testing that the same text you sent in one end comes out the other is all there is to test.
For routes with more data transformation and processing, you could test the output data types, that processors were called when needed, you could mock in throwing of exceptions, etc. What you have above is a good start on that.
Explained in-line,Hope this helps you understand significance of Mock in Unit Test:
public void sampleMockTest() throws InterruptedException {
String expected="Hello";
MockEndpoint mock = getMockEndpoint("mock:output");//Mocking endpoint
mock.expectedBodiesReceived(expected); //Setting expected output to mocked endpoint
String input="Hello";
template.sendBody("direct:sampleInput",input );//triggering route execution by sending input to route
assertMockEndpointsSatisfied(); //Verifies if input is equal to output
}
If your endpoint is Rest service you can make use of "TestRestTemplate" instead of Mocking it and Test like below:
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleCamelApplicationTest {
}
import org.springframework.boot.test.web.client.TestRestTemplate;
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleCamelApplicationTest {
#Autowired
private TestRestTemplate restTemplate;
}
#Test
public void sayHelloTest() {
// Call the REST API
ResponseEntity<String> response = restTemplate.getForEntity("/camel/hello", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
String s = response.getBody();
assertThat(s.equals("Hello World"));
}

Camel: How to mock a route with two endpoints

I'm new to Camel and I need to understand how to unit test my route that has two endpoints. The first endpoints gets a user ID and uses that for the second endpoint.
public RouteBuilder routeBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws HttpOperationFailedException {
this.from(MyServiceConstant.ROUTE)
.setHeader(...)
.setHeader(...)
.to(MyConstants.THE_FIRST_ROUTE)
.setHeader(...)
.setHeader(...)
.process(...)
.setProperty(...)
.to(MyConstants.THE_SECOND_ROUTE)
}
};
}
So I have to mock both the MyConstants.THE_FIRST_ROUTE and MyConstants.THE_SECOND_ROUTE in my Test class. I did that but am not sure how to write the test. All I'm doing is hitting the second endpoint but don't know how to trigger the first.
#Produce(uri = MyServiceConstant.ROUTE)
private MyService myService;
#EndpointInject(uri = "mock:" + MyConstants.THE_FIRST_ROUTE)
private MockEndpoint mockFirstService;
#EndpointInject(uri = ""mock:" + MyConstants.THE_SECOND_ROUTE)
private MockEndpoint mockSecondService;
#Test
#DirtiesContext
public void getDetails()throws Exception {
// **The missing part**: Is this the right way to call my first service?
this.mockFirstService.setUserId("123456");
// this returns a JSON that I'll compare the service response to
this.mockSecondService.returnReplyBody(...PATH to JSON file);
UserDetail userDetailsInfo = this.myService.getUserDetails(...args)
// all of my assertions
assertEquals("First name", userDetailsInfo.getFirstName());
MockEndpoint.assertIsSatisfied();
}
I got some time today to quickly hack around some demo code, with Camel Spring boot archetype. Here we go. My route produces messages from a timer component. Explicit delivery to an endpoint is not used.
//Route Definition - myBean::saySomething() always returns String "Hello World"
#Component
public class MySpringBootRouter extends RouteBuilder {
#Override
public void configure() {
from("timer:hello?period={{timer.period}}").routeId("hello_route")
.transform().method("myBean", "saySomething")
.to("log:foo")
.setHeader("test_header",constant("test"))
.to("log:bar");
}
}
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
public class MySpringBootRouterTest {
#Autowired
SpringCamelContext defaultContext;
#EndpointInject("mock:foo")
private MockEndpoint mockFoo;
#EndpointInject("mock:bar")
private MockEndpoint mockBar;
#Test
#DirtiesContext
public void getDetails() throws Exception {
assertNotNull(defaultContext);
mockBar.expectedHeaderReceived("test_header", "test");
mockBar.expectedMinimumMessageCount(5);
MockEndpoint.setAssertPeriod(defaultContext, 5_000L);
MockEndpoint.assertIsSatisfied(mockFoo, mockBar);
mockFoo.getExchanges().stream().forEach( exchange -> assertEquals(exchange.getIn().getBody(),"Hello World"));
//This works too
//mockBar.assertIsSatisfied();
//mockFoo.assertIsSatisfied();
}
#Before
public void attachTestProbes() throws Exception {
//This is Camel 3.0 API with RouteReifier
RouteReifier.adviceWith(defaultContext.getRouteDefinition("hello_route"), defaultContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
//Hook into the current route, intercept log endpoints and reroute them to mock
interceptSendToEndpoint("log:foo").to("mock:foo");
interceptSendToEndpoint("log:bar").to("mock:bar");
}
});
}
}
Warning to visitors from future: The test case here demonstrates how to intercept log: endpoints with mock: and set expectations on them. The test case may not be testing anything worthwhile.
Here is a link to the Unit Test cases for the Mock component. It shows how to implement tests with mock: endpoints and CamelTestSupport. #Roman Vottner is completely right in his comment.
This test case may be of specific interest to you since it shows how to swap an smtp: endpoint with a mock: endpoint. Additionally, here is official documentation on how to mock existing endpoints (To use them like test probes).
Caveat: Please bear in mind that Camel 3.0 API is quite different from Camel 2.x API, in this region. Good luck!

Testing application restart with persistence with JUnit and Spring

I have an application that uses persistent JMS queues. Imagine I have an application failure after reading a message and before ack'ing it. The persistent queue must provide that message again after the app restarts. How can I implement a junit integration test for this? I'm testing application restart after a (simulated) application crash mid-"transaction".
I've looked at #DirtiesContext as a way to reset all the Spring parts of the app: reading configs, recreating JMS connections. I could have one test case A) write a message, allow the message to be read and then "exit" (shut down the spring context?) without acking. Then another test case (after the context is reloaded B) read the message and assert that it was not lost after the simulated application restart. But the builtin context reload provided by #DirtiesContext only happens between test cases. And JUnit does not provide for a means to sequence two test cases or make B) dependent on A), such that A) will always run (and run first) if you decide to run B).
In a previous life, I wrote manual code that shut down the spring context, and manually restarted a new context. E.g. between A) and B). That could be done within a single test case. It wouldn't play nicely with #RunWith(SpringRunner.class), I'm guessing, and seems pretty old school. Is that really the only option, given all the wonderful Spring and JUnit support these days?
This seems like a pretty useful technique. It could be used to test re-arrival of messages after they've been rolled back (and are stuck on a dead letter queue); or that sequence numbers written to a DB are really persisting during a "crash". Any number of failure cases that wind up affecting the next application startup due to persisted (or not) data. How do we simulate spring restart in junit tests? Either within one test, or create a sequence of dependent tests with #DirtiesContext between them.
The following article How to Restart a Spring Application Context within a JUnit test describes a solution to the question.
The solution consist to expend SpringJUnit4ClassRunner in order to inject a SpringRestarter singleton which takes care of restating the application context.
public class SpringRestarter {
private static SpringRestarter INSTANCE = null;
private TestContextManager testContextManager;
public static SpringRestarter getInstance() {
if (INSTANCE == null) {
INSTANCE = new SpringRestarter();
}
return INSTANCE;
}
public void init(TestContextManager testContextManager) {
this.testContextManager = testContextManager;
}
public void restart(Runnable stoppedLogic) {
testContextManager.getTestContext().markApplicationContextDirty(DirtiesContext.HierarchyMode.EXHAUSTIVE);
if (stoppedLogic != null) {
stoppedLogic.run();
}
testContextManager.getTestContext().getApplicationContext();
reinjectDependencies();
}
private void reinjectDependencies() {
testContextManager
.getTestExecutionListeners()
.stream()
.filter(listener -> listener instanceof DependencyInjectionTestExecutionListener)
.findFirst()
.ifPresent(listener -> {
try {
listener.prepareTestInstance(testContextManager.getTestContext());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner and initialise the singleton SpringRestarter.
public class RestartingSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public RestartingSpringJUnit4ClassRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected TestContextManager createTestContextManager(Class<?> clazz) {
final TestContextManager testContextManager = super.createTestContextManager(clazz);
SpringRestarter.getInstance().init(testContextManager);
return testContextManager;
}
}
RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner in order to extend SpringRunner
public class RestartingSpringRunner extends RestartingSpringJUnit4ClassRunner {
public RestartingSpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
}
Using Within a JUnit 4 Test
#RunWith(RestartingSpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIntegrationTests {
public void myRestartingTest() {
//Some test logic before the context restart
SpringRestarter.getInstance().restart(() -> {/* Some logic after context stopped */});
//Some test logic after the context restart
}
}
Full details of the explanation

Hystrix-javanica -Hystrix Timeout fallbacks are not triggering

Need to use circuit breaker for one of the projects and using hystrix for the purpose. But hystrix fallback are not triggered even after timeouts. Please help if something is been missed. Thank you in advance.
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica
public class TestHystrix {
#HystrixCommand(fallbackMethod="fallbackCallFunc",
commandProperties={
#HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
})
public String callFunc() throws InterruptedException{
Thread.sleep(1050);
return "success";
}
public String fallbackCallFunc(){
return "default";
}
public static void main(String[] args) throws InterruptedException {
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.callFunc.execution.isolation.thread.timeoutInMilliseconds", "500");
TestHysterix testClass = new TestHysterix();
System.out.println(testClass.callFunc());
}
}
For HystrixCommand annotation(Javanica) to work, you need to add Interceptor() module to your service code.
[AOP - Aspect Oriented Programming] functionality.
Working:
Please note here Method interceptor will be used to detect that if the method being called is annotated with HystrixCommand annotation and thereby hystrix code gets executed.
You need to configure Javanica for your project. There are instructions on the javanica wiki
To use Javanica with Spring Boot you can find a short guide at
https://spring.io/guides/gs/circuit-breaker/

What is the equivalent of ExternalResource and TemporaryFolder in JUnit 5?

According to the JUnit 5 User Guide, JUnit Jupiter provides backwards compatibility for some JUnit 4 Rules in order to assist with migration.
As stated above, JUnit Jupiter does not and will not support JUnit 4 rules natively. The JUnit team realizes, however, that many organizations, especially large ones, are likely to have large JUnit 4 codebases including custom rules. To serve these organizations and enable a gradual migration path the JUnit team has decided to support a selection of JUnit 4 rules verbatim within JUnit Jupiter.
The guide goes on to say that one of the rules is ExternalResource, which is a parent for TemporaryFolder.
However, the guide unfortunately doesn't go on to say what the migration path is, or what the equivalent is for those writing new JUnit 5 tests. So what should we use?
Interesting article by author of TemporaryFolderExtension for JUnit5
and
his code repo on github
JUnit5.0.0 is now in general release so let's hope they turn their attention to making the experimental stuff production-ready.
Meanwhile, it seems the TemporaryFolder rule will still work with JUnit5 docs
use this:
#EnableRuleMigrationSupport
public class MyJUnit5Test {
and this:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.0.0</version>
</dependency>
As far as I understood, there can be no one to one mapping from ExternalResource to an equivalent in JUnit5. The concepts just don't fit. In JUnit4, the ExternalResource basically gives you a before and an after callback, but within the rule, you have no control about what before and after actually means. You could use it with #Rule or with #ClassRule.
In JUnit5, the extension is defined to hook in specific extension points and thus the 'when' is well defined.
Another difference in concepts would be, that you can have a state in JUnit4 rules, but your JUnit5 extensions shouldn't have any state. Instead, all state should go to the execution context.
Nevertheless, here is an option I came along with, where before and after relates to each test method:
public abstract class ExternalResourceExtension
implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
#Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
before(context);
}
#Override
public void afterTestExecution(ExtensionContext context) throws Exception {
after(context);
}
protected abstract void before(ExtensionContext context);
protected abstract void after(ExtensionContext context);
}
JUnit 5.4 comes with a built-in extension to handle temporary directories in tests.
#org.junit.jupiter.api.io.TempDir annotation can be used in order to annotate class field or a parameter in a lifecycle (e.g. #BeforeEach) or test method of type File or Path.
import org.junit.jupiter.api.io.TempDir;
#Test
void writesContentToFile(#TempDir Path tempDir) throws IOException {
// arrange
Path output = tempDir
.resolve("output.txt");
// act
fileWriter.writeTo(output.toString(), "test");
// assert
assertAll(
() -> assertTrue(Files.exists(output)),
() -> assertLinesMatch(List.of("test"), Files.readAllLines(output))
);
}
You can read more on this in my blog post, where you will find some more examples on utilizing this built-in extension: https://blog.codeleak.pl/2019/03/temporary-directories-in-junit-5-tests.html.
The documentation for that is still in the making - see pull request #660.
Temporary folders now have a solution in the way of #TempDir. However, what about the idea behind ExternalResources in general? Perhaps it's for a mock database, a mock HTTP connection, or some other custom resource you want to add support for?
The answer, it turns out is you can use the #RegisterExtension annotation to achieve something quite similar.
Example of use:
/**
* This is my resource shared across all tests
*/
#RegisterExtension
static final MyResourceExtension MY_RESOURCE = new MyResourceExtension();
/**
* This is my per test resource
*/
#RegisterExtension
final MyResourceExtension myResource = new MyResourceExtension();
#Test
void test() {
MY_RESOURCE.doStuff();
myResource.doStuff();
}
And here's the basic scaffolding of MyResourceExtension:
public class MyResourceExtension implements BeforeAllCallback, AfterAllCallback,
BeforeEachCallback, AfterEachCallback {
private SomeResource someResource;
private int referenceCount;
#Override
public void beforeAll(ExtensionContext context) throws Exception {
beforeEach(context);
}
#Override
public void afterAll(ExtensionContext context) throws Exception {
afterEach(context);
}
#Override
public void beforeEach(ExtensionContext context) throws Exception {
if (++referenceCount == 1) {
// Do stuff in preparation
this.someResource = ...;
}
}
#Override
public void afterEach(ExtensionContext context) throws Exception {
if (--referenceCount == 0) {
// Do stuff to clean up
this.someResource.close();
this.someResource = null;
}
}
public void doStuff() {
return this.someResource.fooBar();
}
}
You could of course wrap this all up as an abstract class and have MyResourceExtension implement just protected void before() and protected void after() or some such, if that's your thing, but I'm omitting that for brevity.

Categories

Resources