I'm trying to test a Camel Processor in Spring Boot. However I am getting alot of exceptions even when following the guidance from Camel in Action and other SO answers.
I am using Camel 3.0.0-M4
For example here is my test:
#SpringBootTest
#BootstrapWith(SpringBootTestContextBootstrapper.class)
public class MyProcessorTest extends CamelTestSupport {
#Produce
ProducerTemplate template;
#EndpointInject(uri = "mock:out")
private MockEndpoint resultEndpoint;
#Before
public void setUp() throws Exception {
super.setUp();
}
#BeforeClass
public static void stopCamelStart() {
SpringCamelContext.setNoStart(true);
}
#AfterClass
public static void enableCamelStart() {
SpringCamelContext.setNoStart(false);
}
#Before
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("seda:test")
.process(new MyProcessor())
.to("mock:out");
}
};
}
#Test
public void testPrepend() throws Exception {
Map<String, String> body = new HashMap<String, String>();
body.put("test", "body");
template.sendBody("seda:test", body);
String result = resultEndpoint.getExchanges().get(0).getIn().getBody(String.class);
}
}
I get an java.lang.ArrayIndexOutOfBoundsException
If I try and start the camel context context.start(); I get a java.lang.NullPointerException
If I swap the route I sent the body to template.sendBody("mock:out", body); no processing has happened.
If I change from seda:test to direct:test I get a very slow running test and org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: direct://test
Where am I going wrong??
On first view you have two methods marked with #Before. The order of execution of the two in not at all guaranteed by JUnit. So perhaps this is causing trouble.
However, I don't see any need to setup a Camel Route Test to test a Processor.
I highly recommend to replace your Processor(s) (they are clumsy and hard to test) with a POJO and call it with .bean instead of .processor.
Beans are super easy to test
You can use plain JUnit tests which are super fast
You can inject message parts into your methods instead of navigating through the Camel Exchange
Related
I am trying to write JUnit test case to replace my JMS camel route with direct route.
I am trying to replace route "myRoute" with "direct:start". But my camel context is coming as null.
#CamelSpringBootTest
#SpringBootTest(class = Application.class)
#ContextConfiguration
public class AppTest{
#Autowired
CamelContext camelContext;
public void testMethod(){
AdviceWith.adviceWith(camelContext, "myRoute", routeBuilder -> {
routeBuilder.replaceFromWith("direct:start");
});
}
}
I tried to start the camel context in an init() method. But getting below exception from AdviceWith class
#Before
public void init() throws Exception{
camelContext.start();
}
Exception: Getting cannot AdviceRoute as there are no routes
I'm having issues unit testing multiple camel routes within the same class.I have a class which contains several route definitions. e.g. as below:
public class A extends RouteBuilder{
#Override
public void configure() throws Exception {
method1();
method2();
method3();
...
}
private void method1() {
// contains route definitions
final RouteDefinition route = from("direcct:");
route.id("ID1");
route.setHeader("",simple(""));
route.to("direct:B");
route.end();
}
// similarly other methods
}
i want to unit test each method. so have extended CamelTestSupport. can't override the methods in configure when creating routebuilder since the method are private.
this is what i am doing currently
public class A extends CamelTestSupport {
private A route;
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
route = new A();
//can't use below configure since methods are private
//#Override public void configure() throws Exception {
// method1();
//}
//logic for mocking and setting endpoints using mockito
return route;
}
#Test
public void method1_test() throws Exception {
}
}
I can test one method while commenting others(camelcontext loads and starts all the routes in a class).If i don't comment the test fails.
Also for testing other methods within the same test class i cant use the same createRoutebuilder.So below are the questions:
How will i test other methods within the same test class
Is there a way to start and stop routeid so i can test only those routes(tries startroute method but since all the routes are already started it doesn't do anything)
Is there any workaround so that individual methods can be tested
Using camel 3.6, spring boot2 and junit 5
Basically, the question is in the title.
I faced a problem that in post-construct phase my bean (that is autowired in the bean that is going through post-construct phase right now) is already mocked, but all the behavior described by Mockito.when() doesn't work, all the calls return null.
While searching I found this solution.
But is it possible to make it work without using any 3rd party libraries?
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ServiceTest {
#Autowired
#Qualifier("test")
private PCLPortType pclPortType;
#MockBean
private ClearingHelper сlearingHelper;
#MockBean
private OrganizationCacheRepository organizationCacheRepository;
#Before
public void setup() throws Exception{
OperationResultWithOrganizationSystemIdMappingList res = new OperationResultWithOrganizationSystemIdMappingList();
when(clearingHelper.getOrgIdSystemIdMapping(any(Keycloak.class))).thenReturn(res);
}
#Test
public void test() throws Exception{
pclPortType.call("123");
}
}
Test config:
#TestConfiguration
public class TestApplicationConfiguration {
#Bean(name = "test")
public PCLPortType pclPortTypeForTest() throws JAXBException {
...
}
#Bean
public Keycloak keycloak() {
return Mockito.mock(Keycloak.class);
}
}
Component where I want to get mocked beans:
#Component
public class OrganizationCacheJob {
private static final Logger logger =
LogManager.getLogger(OrganizationCacheJob.class);
private final ObjectFactory<Keycloak> factory;
private final ClearingHelper clearingHelper;
private final OrganizationCacheRepository organizationCacheRepository;
#Autowired
public OrganizationCacheJob(ObjectFactory<Keycloak> factory,
ClearingHelper clearingHelper,
OrganizationCacheRepository organizationCacheRepository) {
this.factory = factory;
this.clearingHelper = ClearingHelper;
this.organizationCacheRepository = organizationCacheRepository;
}
#PostConstruct
public void updateCacheRepository() {
doUpdateCacheRepository();
}
#Scheduled(cron = "${organization.cache.schedule}")
public void start() {
logger.info("Starting update organization cache.");
doUpdateCacheRepository();
logger.info("Job finished.");
}
private void doUpdateCacheRepository() {
try {
Keycloak keycloak = factory.getObject();
OperationResultWithOrganizationSystemIdMappingList orgIdSystemIdMapping = clearingHelper.getOrgIdSystemIdMapping(keycloak);
if (orgIdSystemIdMapping != null) {
orgIdSystemIdMapping.getContent().forEach(o -> organizationCacheRepository.saveOrgIdsSystemsIdsMappings(o.getOrgId(), o.getId()));
logger.debug("Was saved {} orgIds", orgIdSystemIdMapping.getContent().size());
}
} catch (Exception e) {
logger.error("Error fetching whole mapping for org and systems ids. Exception: {}", e);
}
}
}
So, in post-construct phase of OrganizationCacheJob I want to get res when calling clearingHelper, but instead I get null.
ClearingHelper is a regular Spring bean marked as a #Component with public methods.
Ahh ok I just realized - when you start your test case, whole env is up and running first, then you advance to testing phase. So, translating to your case - first you got injection and post-constructs called, then #Before method is done, thus the result.
So as you can see, code says more than all the words you could put in your original post.
If it is possible for you, use spies insteed of mocks. If it is not possible to construct that, you will have to redesign your tests to not rely on post construct.
In this case, since you want the same post-construct behavior for every test case, provide own factory method for given mock (like you did with keycloak) and move when-doReturn there. It will be guaranteed that it will happen before post construct.
I have a main route builder:
public class MainRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:a.out").to("activemq:b.in");
from("activemq:b.in").bean(MainMessageConsumer.class);
}
}
I have a second "intercept" route builder:
public class InterceptRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("activemq:a.out").to("activemq:c.in").skipSendToOriginalEndpoint();
from("activemq:c.in").bean(InterceptMessageConsumer.class);
}
}
Both of which are registered to the CamelContext (MainRouteBuilder is registered first, and InterceptRouteBuilder second). However, when I send a message to "activemq:a.out" via:
public class App {
#Produce(uri="activemq:a.out")
private Producer producer;
public void run() {
producer.request("hello");
}
}
The message still arrives on MainMessageConsumer instead of being intercepted. What am I doing wrong?
The interceptor only applies for all routes in the same route builder class. If you want it to work on both, then create a base class, and put the interceptor there, and let the other routes extend your base class, and call its super in the configure method (eg OO inheritance)
Seems to be that if you create your producer using the #Produce annotation, then it won't be intercepted. Whereas if I put:
#Bean
public ProducerTemplate producerTemplate() {
return camelContext().createProducerTemplate();
}
In my application config, and use that instead then it does get intercepted. Not sure if this is the expected behaviour?
I am writing a Rest service using Spring MVC. Here is the outline of the class:
#Controller
public class MyController{
#RequestMapping(..)
public void myMethod(...) throws NotAuthorizedException{...}
#ExceptionHandler(NotAuthorizedException.class)
#ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="blah")
public void handler(...){...}
}
I have written my unit tests using the design posted here. The test is basically as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(....)
public class mytest{
MockHttpServletRequest requestMock;
MockHttpServletResponse responseMock;
AnnotationMethodHandlerAdapter handlerAdapter;
#Before
public void setUp() {
requestMock = new MockHttpServletRequest();
requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
requestMock.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
responseMock = new MockHttpServletResponse();
handlerAdapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void testExceptionHandler(){
// setup ....
handlerAdapter.handle(...);
// verify
// I would like to do the following
assertThat(responseMock.getStatus(), is(HttpStatus.UNAUTHORIZED.value()));
}
}
However, the call to handle is throwing the NotAuthorizedException. I have read that this is by design to be able to unit test that the method throws the appropriate exception, however I would like to write an automated test that the framework is handling this exception appropriately and that the class under test has implemented the handler appropriately. Is there a way to do this?
Please be aware that I do not have access to the actual code in a place where I could post it.
Also, I am limited (for unfortunate reasons) to Spring 3.0.5 or 3.1.2.
Consider using Spring 3.2 and its mvc-test-framework
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")
public class WebMvcTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getFoo() throws Exception {
this.mockMvc.perform(
get("/testx")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isUnauthorized());
}
}
Controller code
#Controller
public class MyController {
public class MyException extends RuntimeException {
};
#RequestMapping("/testx")
public void myMethod() {
throw new MyException();
}
#ExceptionHandler(MyException.class)
#ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "blah")
public void handler() {
System.out.println("handler processed");
}
}
This "test" passes well.
Disclaimer: currently I'm a noob in Spring MVC testing, actually it's my first test.
upd: Thanks to The Drake for the correction.
Annotate your Exception Handling controller with #ControllerAdvice instead of #Controller.
As Boris Treukhov noted when adding the #ExceptionHandler annotation to a method in the controller that throws the exception will make it work but only from that specific controller.
#ControllerAdvice will allow your exception handeling methods to be applicable for your whole application not just one specific controller.
You could change #Test to
#Test(expected=NotAuthorizedException.class)
This would return true if the internals throw up that exception and false otherwise.
This would also make the assertThat() unnecessary. You could write a second test that catches the NotAuthorizedException then you could inspect the responseMock under that condition then.