How to camel route test spring bean with mockito mocked bean - spring

I would like to be able to test a route which consumes from a queue then does some work in a bean involving a spring injected service and use mockito to effectively mock out this service.
My spring route is as follows:
<camel:route id="msgemailqueue-to-emailservice">
<camel:from uri="activemq:emails" />
<camel:bean ref="emailService" method="createEmailRequest"/>
</camel:route>
The emailService bean has an autowired service which is then called in the createEmailRequest() which goes off to another service and retrieves user data to be used subsequently.
The test:
#RunWith(MockitoJUnitRunner.class)
public class TroubledEmailServiceImplTest extends CamelSpringTestSupport {
#Produce(context = "messagingCamelContext")
protected ProducerTemplate producer;
#Mock
private UserRestService userRestService;
#Override
protected AbstractApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("messaging-camel-route-test-context.xml");
}
#Test
public void testUserResponseToEmailQueue() throws Exception {
context.addRoutes(new MyDynamcRouteBuilder(context, "direct:addEmailRequest", "activemq:emails"));
Mockito.when(userRestService.getUserById(Mockito.anyLong())).thenReturn(
new WebServiceResult<UserVO>(new UserVO()));
CreateMessageRequest msgReq = new CreateMessageRequest();
producer.sendBody("direct:addEmailRequest", msgReq);
Mockito.verify(userRestService).getUserById(Mockito.anyLong());
assertMockEndpointsSatisfied();
}
The bean as follows:
#Override
public void createEmailRequest(final CreateMessageRequest request) throws CreateEmailException {
LOGGER.trace("Entering createEmailRequest(request) " + Arrays.asList(new Object[] { request }));
Validate.notNull(request, "CreateMessageRequest was null");
WebServiceResult<UserVO> response;
try {
response = userRestService.getUserById(request.getId());
} catch (final WebServiceException e) {
throw new CreateEmailException("Error lookup up user data for email", e);
}
final UserVO userResponse = response.getData();
All compiles ok and when running the route fires as an object is popped on the queue which is then passed to the bean and the createEmailRequest is invoked and the call to the mockito mocked service happens ok
response = userRestService.getUserById(request.getId());
but the response is null even though
Mockito.when(userRestService.getUserById(Mockito.anyLong())).thenReturn(
new WebServiceResult<UserVO>(new UserVO()));
was performed in the test. It appears that the service in bean is a different instance i.e. mockito mock is never invoked.
I am doing something wrong and perhaps my testing approach is all wrong as well but should this work in theory? I'd really like to be able to mock out a service in a bean in my camel route.

I'm using Camel Enhanced Spring Test and have passed through the same issue. I only changed #Mock to #MockBean. My Camel version is 2.18.

the mock userRestService you create in the test has to be the same instance you use in the bean. I do not see where you are setting the userRestService for the createEmailRequest method. That service needs to be the same mock object as you create in your test.

I have resolved this - mea culpa. My test class was effectively creating two instances of the service - one through the spring application context and another due to the #RunWith(MockitoJUnitRunner.class) plus #mock annotation. Now resolved by doing the mock creation once. To sum up this was a spring wiring issue only on my part. Many thanks #mike-pone.

Related

Spring caching works only sometimes

I have a Spring controller and want to cache the response. When I move the #Cacheable annotation from the getBooks to the doGetBooks method, the caching will stop. Once I move it back to the getBooks method caching works again. Why is that and how can I fix it?
This will cache the public method response
#GetMapping
#Cacheable(value = "cache", key = "{ #root.methodName }")
public Books getBooks(#RequestHeader(value = "user-agent", required = false) String userAgent) throws Exception {
if(valid) {
return this.doGetBooks();
}
throw new Exception();
}
public Books doGetBooks() throws Exception{
...
This will never cache the private method response
#GetMapping
public Books getBooks(#RequestHeader(value = "user-agent", required = false) String userAgent) throws Exception {
if(valid) {
return this.getBooks();
}
throw new Exception();
}
#Cacheable(value = "cache", key = "{ #root.methodName }")
public Books doGetBooks() throws Exception{
...
Problem: You are calling doGetBooks() within the same class, and Spring cache requires an AOP proxy to the called method.
This is a good discussion describing why Spring AOP can not intercept methods called by other class methods: AOP calling methods within methods
There are at least three workarounds:
Refactor the code: Move doGetBooks() into another #Component, and invoke the method using that (injected) bean (refactoredBean.doGetBooks())
Create a self-reference to the service invoking the call (By #Autowired private MyService myservice and invoke myservice.doGetBooks().
Using the ApplicationContext to cast the service bean, and invoking the method on that bean.
Once you invoke a method that Spring Cache can intercept (via AOP), then the #Cacheable() annotation should trigger.

not able to replace spring bean with mock in camel route

using #Profile I am able to mock the spring bean, however in the camel route which mock bean method is not invoked. I am using SpringJUnit4ClassRunner.class and using #ActiveProfile
Below is the route in which I want to replace, cancelSubscriptionTransformer, myBeanClient, extendedClient beans with my mock beans in unit testing.
from("{{cancelSubscriptionFromRMQUri}}").routeId("cancelSubscriptionRoute")
.unmarshal().json(JsonLibrary.Jackson, Subscription.class)
.bean("cancelSubscriptionTransformer", "toKbCancelSubscription")
.choice()
.when().simple("${body.serviceType} == 'subscriptions'")
.bean("myBeanClient", "cancelSubscription(${body.subscriptionId}, ${body.createdBy}, ${body.reason}, ${body.comment})")
.bean("extendedClient", "retrieveSubscription(${body.subscriptionId}, ${body.externalKey})")
.marshal(json)
.to("{{cancelSubscriptionTORMQUri}}")
.when().simple("${body.serviceType} == 'usage'")
.bean("myBeanClient", "cancelSubscription(${body.subscriptionId}, ${body.dateTime},null, null, -1, ${body.createdBy}, ${body.reason}," +
" ${body.comment})")
.endChoice();
Below is how I define my ExtendedClientMock, I use the same approach for the rest of the mock beans
#Profile("test")
#Primary
#Repository
public class ExtendedClientMock extends ExtendedClient {
public Subscription retrieveSubscription(UUID subscriptionid, String sdpSubscriptionId) throws MyClientException {
Subscription subs=new Subscription();
subs.setProductName("test");
return subs;
}
}
Below is the code for unit testing:
#ActiveProfiles({"test", "aop"})
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = CancelSubscriptionRouteTest.class)
#EnableAutoConfiguration
#ComponentScan
#ContextConfiguration(classes = { BillingServicesApplication.class })
#UseAdviceWith
public class CancelSubscriptionRouteTest {
#Autowired
protected CamelContext camelContext;
#Autowired
private CancelSubscriptionTransformer cancelSubscriptionTransformer;
#Autowired
private ExtendedClient extendedClient;
#Autowired
private MyBeanClient myBeanClient;
#EndpointInject(uri = "{{cancelSubscriptionTORMQUri}}")
private MockEndpoint cancelSubscriptionTORMQUriEndpoint;
#EndpointInject(uri = "{{cancelSubscriptionFromRMQUri}}")
private ProducerTemplate cancelSubscriptionFromRMQUriEndpoint;
#Inject
private ObjectMapperContextResolver objectMapperContextResolver;
#Test
#DirtiesContext
public void testCancelSubscriptionRoute() throws Exception {
cancelSubscriptionTORMQUriEndpoint.expectedMessageCount(1);
ObjectMapper objectMapper= objectMapperContextResolver.getContext(ObjectMapperContextResolver.class);
String jsonString=objectMapper.writeValueAsString(subscription);
CancelSubscription cancelSubscription=cancelSubscriptionTransformer.toKbCancelSubscription(subscription);
Assert.assertEquals("mock auto created by amel",cancelSubscription.getComment());
cancelSubscriptionFromRMQUriEndpoint.sendBody(" {{cancelSubscriptionFromRMQUri}}",jsonString);
cancelSubscriptionTORMQUriEndpoint.assertIsSatisfied();
}
}
The Assert.assertEquals("mock auto created by amel",cancelSubscription.getComment()); gets statisfied by calling cancelSubscriptionTransformer.toKbCancelSubscription which is invoked on the mock bean. however when message is sent to cancelSubscriptionFromRMQUriEndpoint.sendBody, the route is invoked and the actual beans in the route are not being replaced by mock beans
#MickaƫlB looks like the issue was I was not extending the correct bean and also I had to use #Inject in my route builder spring bean and use bean name instead of string format of bean name
This is very old but I ran into this issue.
The answer is that instead of .Bean(MyBean.class, "myMethod"), you should use .to("bean:myBean?method=myMethod"). The reason is that the first way, Camel will instantiate the bean. The 2nd way, Spring has control of the bean and camel will look it up. Therefore you can use Spring mockBean to change it.
I'm using Camel version 3 now by the way, and beanRef is removed. If you used beanRef, replace it with .to("bean:myBean?method=myMethod).

How to mock a spring injected service proxy?

I am trying to unit test a Spring MVC based Controller.
This controller calls out to a service. That service is actually remote, and is exposed to the controller via JSON-RPC, and specifically, a com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean
The Controller has:
#Controller
#RequestMapping("/users")
public class UserController {
/**
* ID manager service that will be used.
*/
#Autowired
IdMService idMService;
...
#ResponseBody
#RequestMapping(value = "/{userId}", method = GET)
public UserAccountDTO getUser(#PathVariable Long userId, HttpServletResponse response) throws Exception {
try {
return idMService.getUser(userId);
} catch (JsonRpcClientException se) {
sendJsonEncodedErrorRepsonse(response, se);
return null;
}
}
...
}
The spring configuration provides the IdMService like this:
<!-- Create the proxy for the Access Control service -->
<bean class="com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean">
<property name="serviceUrl" value="${access_control.service.url}" />
<property name="serviceInterface" value="com.phtcorp.service.accesscontrol.IdMService" />
</bean>
Thus, the IdMService that gets injected into the controller is actually a JSON-RPC proxy, implementing the IdMService interface.
I would like to test the controller, but mock the IdMService. I have this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/test-context.xml" })
#SuppressWarnings("javadoc")
public class TestUserController {
#Autowired
private ApplicationContext applicationContext;
private HandlerAdapter handlerAdapter;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Mocked IdMService service;
#Test
public void getUser() throws Exception {
request.setMethod(RequestMethod.GET.name());
request.setRequestURI("/users/1");
HandlerMethod handler = (HandlerMethod) getHandler(request);
handlerAdapter.handle(request, response, handler);
new Verifications() {{
service.getUser(1L); times=1;
}};
}
...
}
However, I find that IdMService that is injected into the controller is not a mock, it is a JsonRpcProxy after all. I have successfully tested a different controller in this manner, but that one does not use a proxy to its service.
So the question is: how do I use jmockit to cause a mock IdMService to be injected into the UserController? Note that I'm not instantiating the UserController myself, anywhere; spring/spring-mvc does that.
Thanks for any help!
If you are unit testing your UserController, why not just instantiate it yourself. Get Spring out of the picture and just test it all on its own.
You are not testing the UserController here so much as testing the Spring wiring of it and the request mapping for it.
Note that I'm not instantiating the UserController myself, anywhere; spring/spring-mvc does that.
This means that you're not writing a unit test. This is testing the spring wiring which makes it an integration test. When writing a unit test you instantiate the class under test and supply the dependencies yourself. This allows you to isolate the logic in the class being tested by providing mocked instances of the classes dependencies. That's where jmockit comes in.
I solved my problem by injecting the mock myself:
#Mocked IdMService service;
#Before
public void setUp() {
controller.setIdMService(service);
...
}

Spring 3.0 junit test DispatcherServlet

I am trying to test my application with junit.
Therefore I've setup the following class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/META-INF/spring/applicationContext-test.xml" )
#TransactionConfiguration
#Transactional
public class DispatcherServletTest extends AbstractJUnit4SpringContextTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private DispatcherServlet dispatcher;
#Before
public void setUp() throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
MockServletConfig config = new MockServletConfig("myapp");
config.addInitParameter("contextConfigLocation","classpath*:webmvc-config.xml");
dispatcher = new DispatcherServlet();
dispatcher.init(config);
}
//test cases
}
So the problem is, that it seems that my dispatcher servlet cannot send any request to any of my controllers.
I think that there is something with the configuration - contextConfigurationLocation.
It looks like he can find the file (otherwise it would throw an exception) , but doesn't load any configuration
The logger says:
org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [http://localhost:8080/myapp/abc]
But I have absolutely no idea what's wrong...
I would appreciate any help!
Thanks in advance
Mines are working fine, try the following tweaks.
if you're using Junit4 no need to extend you test class, the junit runner should do the trick
Load the context config via classpath, and make sure is accessible from the test classpath
#ContextConfiguration(locations={"classpath:applicationContext-test.xml"})
then just test the annotated controllers. I do it like this:
#Test
#Transactional
public void testAnnotatedListUser() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
AnnotationMethodHandlerAdapter handlerAdpt = new AnnotationMethodHandlerAdapter();
request.setRequestURI("/you/URIhere");
ModelAndView mav = handlerAdpt.handle(request, response, this.controller);
assertEquals("Incorrect view name returned", "myexpectedviewname", mav.getViewName());
}
There are several Problems in my question:
At first, it is not possible to extend the AbstractJUnit4SpringContextTests and use #RunWith(...), because it's the same.
At second, you should not use the dispatcherServlert, but an Handler by defining the handler in you application.xml and autowiring it in the test case via #Autowire private Handler handler...
Then everything should work fine!

EasyMock object for unit testing involving scope="request" bean

I am trying to add some Unit Testing to some of our companies code. Yes, I know it should already be there, but not everyone seems to have the same view of unit testing that I do.
However, I have come against a bit of a stopper for me. Admittedly, my Java, Spring and Unit Testing knowledge are not all that they should be. My problem is this though:
I have added a unit test to my code, which tests a class. This class includes a bean which has scope="request", and when it tries to instantiate the bean it throws an exception:
java.lang.IllegalStateException: No Scope registered for scope 'request'
I believe this is because I don't have a HttpServletRequest object, but I don't know how to create a mock one of these and also I don't know how, once created, to add this Mock Object to the unit test so that it resolves this problem.
Below is a cut down version of the code involved, which I believe includes all of the details that are part of this problem.
How can I get this to work?
#Test
public void handleRequest() {
try {
Message<?> outMessage = (Message<?>) response.handleRequest(map);
} catch (Exception e) {
assertNotNull(e);
}
outMessage.getPayload().toString());
}
public class upddResponse extends AbstractResponseTransform {
#SuppressWarnings("unchecked")
public Message<?> handleRequest(Map<String, Message<?>> messages) throws Exception {
super.addEnvironmentDetails(serviceResponseDocument.getServiceResponse());
}
public abstract class AbstractResponseTransform implements ResponseTransform,
ApplicationContextAware {
private ApplicationContext applicationContext;
private MCSResponseAggregator mcsResponseAggregator;
public ServiceResponseType addEnvironmentDetails(ServiceResponseType serviceResponse) throws Exception {
try {
mcsResponseAggregator = (MCSResponseAggregator) applicationContext
.getBean("mcsResponseAggregator");
}
catch (Exception ex) {
}
}
}
public interface ResponseTransform extends Transform {
public Message<?> handleRequest(Map<String, Message<?>> messages)
throws Exception;
}
<bean id="mcsResponseAggregator" class="com.company.aggregator.MCSResponseAggregator" scope="request" />
You need a WebApplicationContext to handle beans with: scope="request"
I recommend to use stub objects with Spring integration tests and use EasyMock without Spring when you test a class isolated.
You can use mocks within the Spring Context:
but that will not solve your problem as it will not make Spring understand scope="request". You can create your own implementation of the request scope, but I'm getting the feeling that you're better off not going through all this trouble.
The easy way out would be to override your request scoped bean in a little test context. You're technically not testing the original context then, but you will be done a lot quicker.
Spring 3.2 comes with support for this. See "Spring MVC Test Framework"

Resources