I'm trying to test an existing Camel route, but it's proving much more difficult than I anticipated. Most of the documentation that I've seen online seems to create a new route in the test, and then test that. How can I test a route that I have already created?
For simplicity, I have created the simplest route that I can think of, but I can't for the life of me figure out how to use that route in a test.
Here's the route:
#Component
public class TestRouteBuilder extends RouteBuilder{
#Override
public void configure() throws Exception {
from("direct:testStart")
.log("Log triggered.")
.to("direct:testEnd");
}
}
How can I use this route in a test?
I'm using Camel with Spring Boot, so my first thought was to inject the route into the test, but that didn't seem to work.
Any help appreciated, thanks.
This can be achieved in multiple ways.. Here is one way:
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveAddLast().to("mock:result");
}
});
You can also use CamelTestSupport:
#Override
public String isMockEndpoints() {
// override this method and return the pattern for which endpoints to mock.
// use * to indicate all
return "*";
}
Related
How do I test a REST Controller that uses Oauth2 (client)? I need to mock the oauth2 and I am stuck. Any help would be appreciated.
Hope that this answer may help.
Actually, when using OAuth2 with a ResourceServerConfiguration, you will have a stateless security, which will throw away any effort in mocking users beforehand.
What you should do to mock users is:
Create a TestOnly loaded ResourceServerConfiguration which overrides your standard one in this way:
public class TestConfiguration extends ResourceServerConfiguration {
#Override
public void configure(ResourceServerSecurityConfigurer res) {
res.stateless(false);
super.configure(resources);
}
}
add the #WithMockUser to the tests:
#Test
#WithMockUser(username ="username_admin", authorities = {"I_AM_LEGEND"})
public void myTestWithUser() throws Exception {
this.mvc.perform(get("/getUsername")).andExpect(content().text().contains("username_admin"));
}
#Test
public void myTestWithNoUser() throws Exception {
this.mvc.perform(get("/getUsername")).andExpect(status().isUnauthorized());
}
NOTE: I wrote the code from memory
I have a simple example of an aspectj implementation in a spring boot app and I'm trying to get each different method working.
If I use the following
#Before("execution(* com.example.test.controllers.ProductController.deleteProduct(..))")
public void specificInterception() {
System.out.println("pointcut interception working");
}
This works fine, and I can see a console output of the println
However, if I use
#Pointcut("execution(* com.example.test.controllers.ProductController.deleteProduct(..))")
public void specificInterception() {
System.out.println("pointcut interception working");
}
It fails to trigger. Why is this not working?
A method annotated with #Pointcut on its own doesn't do anything. It only allows you re-use the same expression multiple times rather than having to redeclare it. I'm not even sure if the method body of such a method will be invoked.
In your case, you can use your pointcut like this:
#Pointcut("execution(* com.example.test.controllers.ProductController.deleteProduct(..))")
public void specificPointcut() {}
#Before("specificPointcut()")
public void specificInterception() {
System.out.println("pointcut interception working");
}
Is there a clean way to detect when a spring-boot application is stopped and perform some action before? Kind of CommandLineRunner for stopping a service
Thanks in advance
Similar to ApplicationReadyEvent you can use ContextClosedEvent:
#Component
public class ContextClosedEventListener {
#EventListener(ContextClosedEvent.class)
public void onContextClosedEvent(ContextClosedEvent contextClosedEvent) {
System.out.println("ContextClosedEvent occurred at millis: " + contextClosedEvent.getTimestamp());
}
}
I've come up with this solution. If you have better one, feel free to share
#Component
public class PortalServiceLifeCycle implements CommandLineRunner {
static final Logger LOGGER = LoggerFactory.getLogger(PortalServiceLifeCycle.class);
#Override
public void run(String... arg0) throws Exception {
LOGGER.info("###START FROM THE LIFECYCLE###");
}
#PreDestroy
public void onExit() {
LOGGER.info("###STOP FROM THE LIFECYCLE###");
}
}
Don't know if you have resolve this problem perfectly. I meet this issue recently, and have got a solution that a little different.
Firstly, my Spring boot Application is a Tomcat embedded one. (The second method of this issue doesn't depends on the web structure. don't mad, my friend.) In this case, it's naturally to get the idea of catch the stop event by register a listener. I do it like this,
#WebListener
public class HelloListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("HelloListener contextInitialized");
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("HelloListener contextDestroyed");
}
}
and, at the same time, add the annotation #ServletComponentScan on your Application class.
Surely, there are some other ways to register a ServletContextListener, and once you registered it, you can get the stop event in the contextDestroyed function.
BUT, that don't match my issue very much. I must catch the stop event BEFORE the Spring Beans being destroyed. And here comes the second solution.
modify your application main method like the follow:
SpringApplication application = new SpringApplication(DemoApplication.class);
application.addListeners(new MyListener());
application.run(args);
and provide the defination of class MyListener:
class MyListener implements ApplicationListener<ContextClosedEvent>{
#Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
// your code here
}
}
NOTE: the second solution has nothing to do with Tomcat or other web container. The ContextClosedEvent isn't introduced in the Spring document, but I found it in the source, it's very useful i think.
I will be very glad if this can help some one.
It depends what you want to do but one thing you could do is have a bean that implements SmartLifecycle and implement the stop method. Whenever the context is being stopped, you'd get a callback. Note that it does not necessarily means that the process is shutting down. If you want to invoke some code when that happens, I'd register a shutdown hook as Sven wrote in a comment.
I am writing a client-server application using Spring on backend and AngularJS on frontend. After writing quite a lot of Spring code I wanted to try AOP to extract logging code from controllers' methods. Unfortunately after adding aspects to my project controllers aren't working as they should.
To describe the simplest case, there's controller:
#RestController
public class AuthenticationController {
#RequestMapping("/authenticate")
public Principal authenticate(Principal user) {
return user;
}
}
I expect the Principal to be injected here by Spring Security and sent to client - it all worked well, on the browser a response with 200 code could be seen and in the data there was authenticated Principal.
Then I introduced the following aspect:
#Aspect
#Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
#Pointcut("execution(* correct.package.path.controllers.impl.*Controller.*(..))")
public void withinControllers() {
};
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapped() {
};
#Before("withinControllers() && requestMapped()")
public void logBefore(JoinPoint joinPoint) {
logger.debug("Entering {}", joinPoint.getSignature().toShortString());
}
}
The advice is executed, the authenticate method is executed, but on the browser a 404 response is visible instead of correct one from before. Eclipse logs don't show any errors and I don't know where to go from there.
I've also noticed that when commenting out the #Before advice, leaving only #Pointcuts, it works well, just like before, but it's obviously useless as I need advices to work.
Can anyone explain what's the reason for such behaviour and how can I fix that?
Regards,
imralav
I have the following route configuration:
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:in").to("direct:out");
}
}
When I try to test it:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpoints
public class MyRouteTest {
#EndpointInject(uri = "mock:direct:out")
private MockEndpoint mockEndpoint;
#Produce(uri = "direct:in")
private ProducerTemplate producerTemplate;
#Configuration
public static class TestConfig extends SingleRouteCamelConfiguration {
#Bean
#Override
public RouteBuilder route() {
return new MyRoute();
}
}
#Test
public void testRoute() throws Exception {
mockEndpoint.expectedBodiesReceived("Test Message");
producerTemplate.sendBody("Test Message");
mockEndpoint.assertIsSatisfied();
}
}
I get this exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint[direct://out].
Exchange[Message: Test Message]
It looks like the Mock is not picking up the message from the endpoint.
What am I doing wrong?
The problem is that mock endpoints just intercept the message before delegating to the actual endpoint. Quoted from the docs:
Important: The endpoints are still in action. What happens differently
is that a Mock endpoint is injected and receives the message first and
then delegates the message to the target endpoint. You can view this
as a kind of intercept and delegate or endpoint listener.
The solution to your problem is to tell certain endpoints (the ones that expect a consumer in your case) not to delegate to the actual endpoint. This can easily be done using #MockEndpointsAndSkip instead of #MockEndpoints:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpointsAndSkip("direct:out") // <-- turns unit test from red to green ;)
public class MyRouteTest {
// ....
}
This issue because, in your route configuration, there is no route with "direct:out" consumer endpoint.
add a line like some thing below,
from("direct:out").("Anything you want to log");
So that direct:out will consume the exchange and In your test, mock will be able check the received text without any issues. Hope this helps !!