camelContext is null in Camel Route Testing while replaceFromWith - java

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

Related

webMvc test not invoking the error handler

I am trying to write the integration test for my controller.
Exception handler is there in a library as part of dependency. But my webMvc Test is not able to invoke the handler
The exception is thrown from method
#WebMvcTest(controllers = MyController.class, excludeAutoConfiguration = {
SecurityAutoConfiguration.class})
#ExtendWith(SpringExtension.class)
public class WebMockTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MyService service;
#Test
public void shall_make_get_call_404_not_ok() throws Exception {
when(service.getById(any())).thenThrow(new CustomException("Not found"));
this.mockMvc.perform(get("/items/" + "12")).andDo(print())
.andExpect(status().isNotFound());
}
}
Here I am not able to get the response but exception is thrown from test method.
How do I trigger the exception handler.
You need to add #Import(Exceptionhandler.class).
I described a similar problem, when using #WebMvcTest with specific controllers and its solution here: https://stackoverflow.com/a/74191408/7789681

#Autowired object is null when working with #SpringBootTest

So I am trying to autowire my http object in my test class and I have tried to integrate with #SpringBootTest however my http object still remains null.
My test class looks like this.
//#RunWith(SpringRunner.class)
#SpringBootTest(classes=Http.class)
public class GetItemTests {
private static final Logger LOGGER = LoggerFactory.getLogger(GetItemTests.class);
#Autowired
private Http httpClass;
}
My SpringBootMain class looks like this
#SpringBootConfiguration
#SpringBootApplication
public class SpringBootMain implements CommandLineRunner {
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class,TokenUtilityClass.class, Paypal.class);
}
#Override
public void run(String... args) throws Exception {
//test.authenticationToken();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class);
}
}
I have tried running with the SpringRunner as well as this but I receive errors about failing to load the application context.
Unless your Http class is annotated with #Component (meaning it is a bean maintained by the Spring IoC Container) you will not be able to #Autowire it in the way you wrote.
Please also post the exception you are getting and the implementation of your Http class so we can potentially provide further help.

Test Camel Processor in Spring Boot

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

Mock class inside REST controller with Mockito

I have a spring-boot application which exposes a REST interface via a controller. This is an example of my controller:
#RestController
public class Controller {
#Autowired
private Processor processor;
#RequestMapping("/magic")
public void handleRequest() {
// process the POST request
processor.process();
}
}
I am trying to write unit tests for this class and I have to mock the processor (since the processing takes very long time and I am trying to avoid this step during testing the controller behavior). Please note, that the provided example is simplified for the sake of this question.
I am trying to use the mockito framework for this task:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
#Test
public void testControllerEmptyBody() throws Exception {
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
}
}
However, this fails with
java.lang.IllegalArgumentException: Could not find field [processor] of type [null] on target [class org.company.Controller]
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:112)
...
Could please someone give me a hint, how this mock could be injected in my controller?
Shouldn't you be passing an instance to set the field on, rather than the class, e.g.:
...
#Autowired
private Controller controller;
...
#Before
public void setUp() throws Exception {
...
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(controller, "processor", processor);
}
I think that you can inject directly the mock like:
#InjectMocks
private ProcessorImpl processorMock;
And remove this line:
ReflectionTestUtils.setField(Controller.class, "processor", processor);
See Injection of a mock object into an object to be tested declared as a field in the test does not work using Mockito?
Rework your controller to use constructor injection instead of field injection. This makes the dependency explicit and makes your test setup drastically simpler.
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
//This line should be added to perform mock for Processor.
Mockito.when(processor.process()).thenReturn(<Your returned value>);
//ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
In above put the your returned value for "Your returned value" and in test use this value to verify your output.
You can remove servlet context class in SpringApplicationConfiguration and mock servlet context. There is no need for injection of WebApplicationContext and ReflectionTestUtils.
Basically, your code should look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#InjectMocks
private MyController controller;
#Mock
private Processor processor;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testControllerEmptyBody() throws Exception {
when(proessor.process()).thenReturn(<yourValue>);
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
verify(processor, times(<number of times>)).process();
}
}
Processor will be mocked and mock will be injected into controller.

Register #ControllerAdvice annotated Controller in JUnitTest with MockMVC

My #ControllerAdvice annotated Controller looks like this:
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
#ExceptionHandler(AuthenticationException.class)
public void authenticationExceptionHandler() {
}
}
Of course my development is test driven and I would like to use my exception Handler in the JUnit Tests. My Test case looks like this:
public class ClientQueriesControllerTest {
private MockMvc mockMvc;
#InjectMocks
private ClientQueriesController controller;
#Mock
private AuthenticationService authenticationService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void findAllAccountRelatedClientsUnauthorized() throws Exception {
when(authenticationService.validateAuthorization(anyString())).thenThrow(AuthenticationException.class);
mockMvc.perform(get("/rest/clients").header("Authorization", UUID.randomUUID().toString()))
.andExpect(status().isUnauthorized());
}
}
Probably I need to register the ControllerAdvice Class. How to do that?
Since Spring 4.2, you can register your ControllerAdvice directly into your StandaloneMockMvcBuilder:
MockMvcBuilders
.standaloneSetup(myController)
.setControllerAdvice(new MyontrollerAdvice())
.build();
In order for the full Spring MVC configuration to get activated, you need to use MockMvcBuilders.webAppContextSetup instead of MockMvcBuilders.standaloneSetup.
Check out this part of the Spring documentation for more details.
Your code would look like:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("test-config.xml")
public class ClientQueriesControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private AuthenticationService authenticationService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void findAllAccountRelatedClientsUnauthorized() throws Exception {
when(authenticationService.validateAuthorization(anyString())).thenThrow(AuthenticationException.class);
mockMvc.perform(get("/rest/clients").header("Authorization", UUID.randomUUID().toString()))
.andExpect(status().isUnauthorized());
}
}
Then inside test-config.xml you would add a Spring bean for AuthenticationService that is a mock.
<bean id="authenticationService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="your.package.structure.AuthenticationService"/>
</bean>
You could of course use profiles to inject the mock AuthenticationService in the tests if want to reuse your regular Spring configuration file instead of creating test-config.xml.
UPDATE
After digging around a bit, I found that StandaloneMockMvcBuilder returned by (MockMvcBuilders.standaloneSetup) is totally customizable. That means that you can plug in whatever exception resolver you prefer.
However since you are using #ControllerAdvice, the code below will not work.
If however your #ExceptionHandler method was inside the same controller the code all you would have to change is the following:
mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(new ExceptionHandlerExceptionResolver()).build();
UPDATE 2
Some more digging gave the answer to how you can register a correct exception handler when you are also using #ControllerAdvice.
You need to update the setup code in the test to the following:
#Before
public void setUp() throws Exception {
final ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
//here we need to setup a dummy application context that only registers the GlobalControllerExceptionHandler
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerBeanDefinition("advice", new RootBeanDefinition(GlobalControllerExceptionHandler.class, null, null));
//set the application context of the resolver to the dummy application context we just created
exceptionHandlerExceptionResolver.setApplicationContext(applicationContext);
//needed in order to force the exception resolver to update it's internal caches
exceptionHandlerExceptionResolver.afterPropertiesSet();
mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(exceptionHandlerExceptionResolver).build();
}
Got past the NestedServletException with the following solution...
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", GlobalControllerExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
mockMvc = MockMvcBuilders.standaloneSetup(controller).
setHandlerExceptionResolvers(webMvcConfigurationSupport.handlerExceptionResolver()).
build();
If you have multiple advice classes, each with #ExceptionHandler and one of those classes is handling a very generic base exception, like #ExceptionHandler({Exception.class}), then you will need to add some priority ordering to your advice classes per this SO answer
https://stackoverflow.com/a/19500823/378151
If you are using junits older version than 5 and can not upgrade it for any reason then consider defining like below:
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", MyExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
mockMvc = MockMvcBuilders.standaloneSetup(myController).
setHandlerExceptionResolvers(webMvcConfigurationSupport.handlerExceptionResolver()).
build();
You can add this to your test class
#Autowired
#Qualifier("handlerExceptionResolver")
void setExceptionResolver(HandlerExceptionResolver resolver)
{
this.exceptionResolver = resolver;
}
and then add the exceptionResolver to your MockMvc
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(this.exceptionResolver).build();
}

Categories