I am writing a JUnit test case for a Route class. I'm facing a problem while mocking ServiceClass inside the Processor class.
public class SaveRouteTest extends CamelTestSupport {
private Exchange exchange;
protected ProducerTemplate template;
private SaveRequestBuilder saveRequestBuilder;
private SaveRoute route;
private SaveProcessor saveProcessor;
private ApplicationContext springContext = createApplicationContext();
#Mock
SaveServiceClient saveServiceClient;//Not able to mock this class
#BeforeClass
public void prepareTestCamelContext() throws Exception {
route = springContext.getBean("saveRoute", saveRoute.class);
saveProcessor = springContext.getBean("saveProcessor",
SaveProcessor.class);
saveRequestBuilder = springContext.getBean("saveRequestBuilder",
SaveRequestBuilder.class);
}
#BeforeMethod
public void init() throws SQLException, ServiceException {
MockitoAnnotations.initMocks(this);
exchange = new DefaultExchange(context);
}
#Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
template = context.createProducerTemplate();
return new RouteBuilder[]{route};
}
#Test
public void testHotelCommitTransactionRouteSuccessReturn() throws
Exception {
when(saveServiceClient.invokeServiceWithName(anyObject()).
thenReturn("Response");
exchange.getIn().setBody("Request detail");
exchange = template.send("direct:SaveRoute",exchange);
}
protected ApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("classpath*:config/spring/testContext.xml");
}
}
#Component
public class SaveRoute extends SPRouteBuilder {
#Autowired
private SaveProcessor saveProcessor;
#Override
public void configure() throws Exception {
from("direct:SaveRoute")
.routeId("save")
.to("direct:ProcessSaveFlow")
.end();
from("direct:ProcessSaveFlow")
.process(saveProcessor)
.end();
}
}
public class SaveProcessor implements Processor {
#Autowired
SaveServiceClient saveServiceClient;
#Override
public void process(Exchange exchange) throws Exception {
//This line of code not able to mock
String response = saveServiceClient.invokeServiceWithName(exchange);
exchange.getIn().setBody(response);
}
}
How to resolve mocking of saveServiceClient.invokeServiceWithName? The debugger is always going inside this method. I tried using both mock objects and an injected mock. I can't make the method call directly.
You are creating a mock object, however you are not injecting it anywhere (normally you are doing it with #InjectMocks annotation - read about it).
I think there are several possibilities:
Provide a #MockBean object, which will be considered as a bean candidate in context.
There is a code example for mocking beans.
#RunWith ( CamelSpringRunner.class )
#SpringBootTest
public class RouteBuilderTest extends CamelSpringTestSupport {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ServiceClient serviceClient;
#Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
super.setUp();
}
#Override
public void tearDown() {
}
#Test
public void test() {
when( serviceClient.doStuff() ).thenReturn( "mockedResponse" );
}
}
Mock SaveProcessor and inject it to Route class - you shouldn't take care of ServiceClient, because you are trying to test too much. Tests for SaveProcessor should be separated, tests for route don't need this logic.
Related
This is the method that i am trying to test:
#Singleton
public class PriorityJobQueueService {
public void registerIndividualJob(String jobCode) throws InterruptedException {
List<PriorityJobMapDTO> priorityJobMapDTOS = CDI.current().select(JobGroupsMasterService.class).get().getJobForCronScheduler(jobCode);
priorityJobMapDTOS = validateStrictJobs(priorityJobMapDTOS);
triggerJob(priorityJobMapDTOS);
}
}
This is the skeleton structure of my test file:
#RunWith(MockitoJUnitRunner.class)
public class PriorityJobQueueServiceTest {
#Before
public void beforeTest() throws Exception {
fixture = new Fixture();
}
#Test
public void registerIndividualJob_SUCCESS() throws InterruptedException {
}
private class Fixture {
#InjectMocks
PriorityJobQueueService priorityJobQueueService;
private Fixture() throws Exception {
MockitoAnnotations.initMocks(this);
}
}
}
Now, what i need to do is mock CDI.current.select() statement so that i can run the test.
The only things i've found so far is having to add additional dependencies to my project with the following libraries:
Quarkus
cdi-unit
So are there any other ways to achieve this?
I would change the code to be composable. That's the entire point of Dependency Injection :)
#Singleton
public class PriorityJobQueueService {
#Inject
private JobGroupsMasterService jobGroupsMasterService;
public void registerIndividualJob(String jobCode) throws InterruptedException {
List<PriorityJobMapDTO> priorityJobMapDTOS = jobGroupsMasterService.getJobForCronScheduler(jobCode);
priorityJobMapDTOS = validateStrictJobs(priorityJobMapDTOS);
triggerJob(priorityJobMapDTOS);
}
}
Now your test will look like
#RunWith(MockitoJUnitRunner.class)
class PriorityJobQueueServiceTest {
#Mock
private JobGroupsMasterService jobGroupsMasterService;
#InjectMocks
private PriorityJobQueueService priorityJobQueueService;
#Test
void registerIndividualJob_SUCCESS() throws InterruptedException {
priorityJobQueueService.registerIndividualJob(...);
}
}
cheers, good luck!
My application context is not closed after test method.
I use Junit 5.3.1, spring 5.1.0.RELEASE for Selenium WebDriver tests.
This is my bean:
#Configuration
public class WebDriverConfig {
// ... Some Code ...
#Bean(destroyMethod = "quit")
#Primary
public DelegatingWebDriver cleanWebDriver(WebDriver driver) throws Exception {
driver.manage().deleteAllCookies();
driver.manage().window().maximize();
return new DelegatingWebDriver(driver);
}
// ... Some more code ...
}
This is my class:
#ExtendWith({SpringExtension.class})
#ContextConfiguration(classes = { WebDriverConfig.class, LoggerConfig.class, EmailConfig.class})
#TestExecutionListeners(listeners= {ScreenshotTaker.class, DependencyInjectionTestExecutionListener.class, TestListener.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class BasicScenariosIT {
#Inject
private DelegatingWebDriver driver;
#SuppressWarnings("unused")
#Inject
private URI baseUrl;
#Inject
private Logger logger;
private DelegatingExtentTest testCase;
// ... Some tests ...
}
I expect the line:
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
to close the application context and fire up the line:
#Bean(destroyMethod = "quit")
In my case, call method "quit" close the browser and start a new one. However it doesn't seem to happen.
Would appreciate the help
Well, I found a workaround.
I implemented spring test listeners, and in the listener I marked the context as dirty, instead of relying on the #DirtiesContext annotation.
The listener looks like this:
#ExtendWith({SpringExtension.class})
#ContextConfiguration(classes = { WebDriverConfig.class, LoggerConfig.class})
#TestExecutionListeners(listeners= {DependencyInjectionTestExecutionListener.class})
public class RunnerExtension extends AbstractTestExecutionListener {
#Autowired
protected Logger logger;
#Autowired
protected DelegatingWebDriver driver;
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this);
}
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void beforeTestExecution(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void afterTestExecution(TestContext testContext) throws Exception {
// .. some code here ..
}
#Override
public void afterTestMethod(TestContext testContext) throws IOException {
// .. some code here ..
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
#Override
public void afterTestClass(TestContext testContext) throws IOException {
// .. some code here ..
}
}
The important code line is:
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
It marks the context as dirty and a new context will be created in the next session.
I have a class called SomeBean and two tests that are configured with Stubs for different scenarios. I am using Spring Boot.
The second test is supposed to pass without Exception because there is no stubbing that I did to throw Exception.
The DirtiesContext is not working as well. If I remove the commented code in Test2.java I get the test to pass. I would like to remove the unnecessary subbing by using something similar to DirtiesContext.
I may be missing something basic. Can someone point to what I am doing incorrect.
#Service
public class SomeBeanProcessor {
#Autowired
private BeanValidator beanValidator;
public ResultBean process(SomeBean sb) throws ValidationException{
beanValidator.validateBean(sb);
//Do some processing and return ResultBean;
}
}
Test1.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test1.Test1Config.class) {
public class Test1 {
#Configuration
static class Test1Config {
#Bean
public BeanValidator getSomeRequestValidator() {
return new BeanValidator() {
public void validateBean(SomeBean bean) throws ValidationException {
throw new ValidationException("Validation failed");
}
};
}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestValidationErrors() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
Assert.fail("Should throw exception");
}
}
Test2.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test2.Test2Config.class) {
public class Test2 {
#Configuration
static class Test2Config {
//#Bean
//public BeanValidator getSomeRequestValidator() {
// return new BeanValidator() {
// public void validateBean(SomeBean bean) throws ValidationException { //Do nothing
// }
// };
//}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestSuccess() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
}
}
I have a controller
#RestController
public class Create {
#Autowired
private ComponentThatDoesSomething something;
#RequestMapping("/greeting")
public String call() {
something.updateCounter();
return "Hello World " + something.getCounter();
}
}
I have a component for that controller
#Component
public class ComponentThatDoesSomething {
private int counter = 0;
public void updateCounter () {
counter++;
}
public int getCounter() {
return counter;
}
}
I also have a test for my controller.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ForumsApplicationTests {
#Test
public void contextLoads() {
Create subject = new Create();
subject.call();
subject.call();
assertEquals(subject.call(), "Hello World 2");
}
}
The test fails when the controller calls something.updateCounter(). I get a NullPointerException. While I understand it's possible to add #Autowired to a constructor I would like to know if there is anyway to do this with an #Autowired field. How do I make sure the #Autowired field annotation works in my test?
Spring doesn't auto wire your component cause you instantiate your Controller with new not with Spring, so Component is not instatntiated
The SpringMockMvc test check it correct:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CreateTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Test
public void testCall() throws Exception {
//increment first time
this.mvc.perform(get("/greeting"))
.andExpect(status().isOk());
//increment secont time and get response to check
String contentAsString = this.mvc.perform(get("/greeting"))
.andExpect(status().isOk()).andReturn()
.getResponse().getContentAsString();
assertEquals("Hello World 2", contentAsString);
}
}
The #Autowired class can be easily mocked and tested with MockitoJUnitRunner with the correct annotations.
With this you can do whatever you need to do with the mock object for the unit test.
Here is a quick example that will test the Create method call with mocked data from ComponentThatDoesSomething.
#RunWith(MockitoJUnitRunner.class)
public class CreateTest {
#InjectMocks
Create create;
#Mock
ComponentThatDoesSomething componentThatDoesSomething;
#Test
public void testCallWithCounterOf4() {
when(componentThatDoesSomething.getCounter()).thenReturn(4);
String result = create.call();
assertEquals("Hello World 4", result);
}
}
Use Mockito and inject a mock that you create. I would prefer constructor injection:
#RestController
public class Create {
private ComponentThatDoesSomething something;
#Autowired
public Create(ComponentThatDoesSomething c) {
this.something = c;
}
}
Don't use Spring in your Junit tests.
public CreateTest {
private Create create;
#Before
public void setUp() {
ComponentThatDoesSomething c = Mockito.mock(ComponentThatDoesSomething .class);
this.create = new Create(c);
}
}
I followed guide here, and I was successfully able to configure a producer on my bean endpoint like this:
#Produce( uri = "activemq:foo" )
private MyListener myListener;
MyListener is:
public interface MyListener {
#InOnly
public void send( String message );
}
and my bean:
public class MyBeanEndpoint {
#Produce( uri = "activemq:foo" )
private MyListener myListener;
#Handler
public void doSomething( final Object body ) {
...
}
public void setMyListener( final MyListener myListener ) {
this.myListener = myListener;
}
Now, how can I test this?
I mean: my test extends CamelTestSupport and I configured my routes with
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() { ... }
That is: I've reproduced camel context, but I've NO spring context configured and I want (if possible) to avoid instantiating it.
How can I mock producer or make Camel instantiate and inject this bean into my bean endpoint?
What is the best way to test such situation using Apache Camel features like CamelTestSupport and similar utilities?
My reference test is:
public class Test extends CamelTestSupport {
private static BeanEndpoint beanEndpoint
#BeforeClass
public static void init() {
beanEndpoint.setActivemqMyListener( ??? );
}
#Override
public CamelContext createCamelContext() {
context = new DefaultCamelContext();
context.addComponent( "activemq", new SedaComponent() );
return context;
}
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from( "activemq:foo" )
.to( "mock:out" );
}
};
}
#Test
public void testFooQueue() throws Exception {}
Let Camel create your bean, then the various dependency injection and whatnot is configured for you.
private static BeanEndpoint beanEndpoint
...
beanEndpoint = camelContext.getInjector().newInstance(BeanEndpoint.class);