mocking when multiple RestTemplate - spring-boot

I have multiple resttempletes in configured for my springboot project.
I am unable to mock individual resttemplate for my test case.
I tried with same name mock, the mocking is not happening.
Here is my configuration
#Bean
public RestTemplate restTemplateA(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(2000)
.setReadTimeout(5000)
.basicAuthorization(aUsername, aPassword)
.build();
}
#Bean
public RestTemplate restTemplateB(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(20000)
.setReadTimeout(50000)
.build();
}
#Bean
public RestTemplate restTemplateC(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(20000)
.basicAuthorization(bUsername, bPassword)
.setReadTimeout(50000)
.build();
}
Actual call for resttemplate, which works fine and we need to write test case is:
ResponseEntity<AClass> responseEntity = restTemplateB.exchange(uriBuilder.toUriString(),
HttpMethod.GET, entity, AClass.class);
Mockito.when is not working and actual call to the url happens and connection exception occurs.
#Mock
private RestTemplate restTemplateB;
#Test
public void test_B() throws Exception {
AClass response = gson.fromJson(
FileUtils.readFileToString(ResourceUtils.getFile("classpath:sample/AJson.json"),
Charset.forName("utf-8")),
AClass.class);
ResponseEntity<AClass> responseEntity = new ResponseEntity<>(response,
HttpStatus.ACCEPTED);
Mockito.when(restTemplateB.exchange(Mockito.any(), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class),
Mockito.<ParameterizedTypeReference<AClass>>any()))
.thenReturn(responseEntity);
service.testMethod("abc");
assertNotNull(responseEntity.getBody());
}
Exception:
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://abc/": Connect to abc:80 [abc/10.25.100.11] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException: Connect to abc:80 [abc/101.252.110.100] failed: Connection refused: connect

Is this a unit or an integration test?
If this is a unit test, the name of the bean does not matter, just inject the mocked bean into the class you are trying to test when creating the underTest (service) instance.
If this is an integration test, do not use #Mock, use #MockBean to inject the mocked bean into the application context (so that it can override the beans in your context).
MockBean also has a name parameter that can be used to override a specific named bean.

Related

Unable to mock RestTemplate using JUnit 5 in Spring Boot

Trying to mock restTemplate postForEntity() but it is returning null instead of ResponseEntity object I am passing inside thenReturn().
Service Impl class
public ResponseEntity<Object> getTransactionDataListByAccount(Transaction transaction) {
ResponseEntity<Object> transactionHistoryResponse = restTemplate.postForEntity(processLayerUrl, transaction, Object.class);
return new ResponseEntity<>(transactionHistoryResponse.getBody(), HttpStatus.OK);
}
Inside Test class
#SpringBootTest
#ActiveProfiles(profiles = "test")
public class TransactionServiceImplTest {
#MockBean
private RestTemplate mockRestTemplate;
#Autowired
private TransactionServiceImpl transactionService;
#Test
public void getTransactionDataListByAccountTest() throws Exception{
Transaction transaction = new Transaction();
transaction.setPosAccountNumber("40010020070401");
ArrayList<Object> mockResponseObj = new ArrayList<Object>(); //filled with data
ResponseEntity<Object> responseEntity = new ResponseEntity<Object>(mockResponseObj, HttpStatus.OK);
when(mockRestTemplate.postForEntity(
ArgumentMatchers.anyString(),
ArgumentMatchers.eq(Transaction.class),
ArgumentMatchers.eq(Object.class))).thenReturn(responseEntity);
// THROWING NullPointerException at this line.
ResponseEntity<Object> actualResponse = transactionService.getTransactionDataListByAccount(transaction);
System.out.println("--- Response ---");
System.out.println(actualResponse);
}
Error
While executing test case, actual service is getting called. When it tries to invoke resttemplate inside service impl class it is returning null.
Trying to call getBody() on transactionHistoryResponse throwing NullPointerException
In your mock setup, the matcher ArgumentMatchers.eq(Transaction.class) will only match if the argument you pass in is the Class<Transaction> object Transaction.class. This is not what you want; you want it to match anything of type Transaction. To do this, use ArgumentMatchers.any(Transaction.class).
This answer has a good explanation.

How to test complete IntegrationFlow from MessagingGateway to ServiceActivator

In our Spring Boot project we have the following IntegrationFlow configuration
package our.configs;
#Configuration
#EnableIntegration
public class MessageChannelsConfiguration {
public static final String OUTBOUND_CHANNEL = "outboundChannel";
public static final String OUTBOUND_CHANNEL_GROUP_ID = "outboundMessageGroup";
#Bean
IntegrationFlow outboundSnapshotMessageChannel(ChannelMessageStore outboundChannelMessageStore,
OutboundFixMessageService outboundMessageService) {
return f -> f
.channel(c -> c.queue(
OUTBOUND_CHANNEL,
outboundChannelMessageStore,
OUTBOUND_CHANNEL_GROUP_ID))
.handle(outboundMessageService, "processOutboundMessage");
}
#Bean
OutboundMessageService outboundFixMessageService(ObjectMapper objectMapper){
return new OutboundMessageService(objectMapper);
}
#Bean
ChannelMessageStore outboundChannelMessageStore(#Qualifier("dataSource") DataSource dataSource,
ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider) {
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(dataSource);
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(channelMessageStoreQueryProvider);
jdbcChannelMessageStore.setRegion("TX_TIMEOUT");
return jdbcChannelMessageStore;
}
#Bean
#Profile({"test"})
ChannelMessageStoreQueryProvider jdbcChannelMessageStoreQueryProvider() {
return new H2ChannelMessageStoreQueryProvider();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller(TransactionManager transactionManager,
Initiator clientInitiator) {
return Pollers.fixedRate(500))
.maxMessagesPerPoll(1)
.advice(transactionInterceptor(transactionManager), new CheckSessionPollingAdvise(clientInitiator))
.get();
}
private TransactionInterceptor transactionInterceptor(TransactionManager transactionManager) {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager)
.propagation(Propagation.NESTED)
.build();
}
}
and the messaging Gateway which is defined as in a separate package then the above configuration
package our.businesslogic;
#MessagingGateway
public interface OutboundMessageGateway {
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendMarkerMessage(Object markerMessage,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
#Gateway(requestChannel = MessageChannelsConfiguration.OUTBOUND_CHANNEL)
void sendOrderMessage(Order message,
#Header(ChannelMessageHeaders.RECIPIENT_ID) String institutionId,
#Header(ChannelMessageHeaders.MESSAGE_TYPE) ChannelMessageType channelMessageType);
}
I want to test the behavior of the complete flow including the persistence to the JdbcChannelMessageStore (and later also the transactional scenarios) with JUnit5.
E.g.
#Test
void whenSendTrancheMessage_givenPollingBlockedByAdvise_thenCorrectNumberOfMessagesOnQueue() {
//given
String recipientId = "Mocked-recipient";
List<Order> orders = Arrays.asList(
new Order(),
new Order(),
new Order(),
new Order()
);
//when
clientInitiator.stopConnection(); // Queue will not be read as long as
// there is no connection
orders.forEach(order->
outboundMessageGateway.sendOrderMessage(order,recipientId,ChannelMessageType.SNAPSHOT));
//then
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
}
#Test
void whenSendTrancheMessage_givenPollingIsNotBlocked_thenMessagesAreReceivedByHandler() {
//some test code with mocked ServiceActivator
}
I have tried with two different ways
as an Integration test with #SpringBootTest
as a context specific JUnit test with #ContextConfiguration and #SpringIntegrationTest
In case of 1) my tests are working when called separately, but are failing with the following exception when they are run together with existing integration tests
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class p
ath resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation vi
a factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servl
et.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.h2.jdbc.JdbcSQLNonTransientConnection
Exception: Exception opening port "9092" (port may be in use), cause: "java.net.BindException: Address already in use: JVM_Bind" [900
61-199]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:483)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowir
eCapableBeanFactory.java:1336)
In case of 2) the following exception is thrown showing problems with the outboundMessageGateway
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
... ... ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'outboundMessageGateway': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [our.businesslogic.OutboundMessageGateway]: Specified class is an interface
I would appreciate it very much if someone could help me to solve this issues.
Exception opening port "9092"
You probably need to add a #DirtiesContext along side with the mentioned #SpringBootTest.
Failed to instantiate [our.businesslogic.OutboundMessageGateway]
If you don't use Spring Boot in your test environment, you must be explicit #EnableIntegration and #IntegrationComponentScan must be there to Spring Integration infrastructure available.
See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#configuration-enable-integration
Assertions.assertThat(outboundChannelMessageStore.messageGroupSize(FixMessageChannelsConfiguration.OUTBOUND_CHANNEL_GROUP_ID))
.isEqualTo(orders.size());
You can't check this if your handle(outboundMessageService, "processOutboundMessage") is not stopped. Since you have a poller for the queue channel, it is going to pull all the message from the store and lets that handler to process them. So, there is going to be nothing in the store at the moment you try to verify it (or some wrong unexpected number).

Understanding the instance variable names and the methods to create it using Spring #Bean annotation

I have written a simple Spring Boot Application, which I would later extend to build a Spring REST client. I have a working code; I have tried to change a few instance variable names and method names and playing around.
Code:
#SpringBootApplication
public class RestClientApplication {
public static void main(String[] args) {
SpringApplication.run(RestClientApplication.class, args);
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
RestClientApplication.class)) {
System.out.println(" Getting RestTemplateBuilder : " + ctx.getBean("restTemplateBuilder"));
System.out.println(" Getting RestTemplate : " + ctx.getBean("restTemplate"));
}
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
#Bean
public CommandLineRunner runner() {
return args -> { SOP("hello"); }
}
}
Observations:
The instance variable names follow the camel-case notation, as
expected. So, restTemplate and restTemplateBuilder works.
While creating a RestTemplate instance through restTemplate() method, I tried changing the name of the argument to builder. It works.
While creating a RestTemplate instance through restTemplate() method, I tried changing the name of the method to a random one and I get an exception that "No bean named 'restTemplate' available".
CommandLineRunner interface is implemented through a lambda expression. Accessing commandLineRunner throws an exception.
Question
Why do I see the results mentioned in point #2 and #3?
While creating a RestTemplate instance through restTemplate() method,
I tried changing the name of the argument to builder. It works.
This works because, By default spring autowire's by type. So it searches for a bean with type RestTemplateBuilder and it finds it and hence no error.
While creating a RestTemplate instance through restTemplate() method,
I tried changing the name of the method to a random one and I get an
exception that "No bean named 'restTemplate' available".
You are getting an exception not because you changed the method name, but because of this
ctx.getBean("restTemplate")
Because by default #Bean uses method name as name of the bean. (check this). So the name of the bean of type RestTemplate which is returned by your random method is name of your random method. Hence when you try to get a bean with name restTemplate, it throws exception.
But if you were to Autowire a bean of type RestTemplate, it would still work, because Spring will Autowire by type by default and it knows a bean of type RestTemplate(name as random method name).

Getting I/O error while executing JUnit test case for Spring Controller

I am executing a test case to call spring controller (GET method). However, It throws below I/O error.
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8039": Connect to localhost:8039 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException: Connect to localhost:8039 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:674)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:636)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
Below is the test case class that i am executing which throws the above error.
public class GetRuleSetsTests extends PreferencesAdminClientTestApplicationTests<GetRuleSetsResponse>{
#Test
public void testSuccess() throws Exception
{
final String mockedResponseJson = rawJsonFromFile("com/cnanational/preferences/client/rule-sets/getRuleSetsResponse.json");
MockRestServiceServer mockServer = mockServer();
mockServer.expect(requestTo(dummyUri()))
.andExpect(method(HttpMethod.GET))
.andExpect(queryParam("ruleSetDescription", "TestRuleDescription"))
.andRespond(withSuccess(
mockedResponseJson,
MediaType.APPLICATION_JSON));
ServiceClientResponse<GetRuleSetsResponse> response = executeDummyRequest();
mockServer.verify();
assertThat(response.isSuccessful(), equalTo(true));
GetRuleSetsResponse programResponse = response.getParsedResponseObject();
assertThat(programResponse.getRuleSets().size(), equalTo(2));
}
#Override
public URI dummyUri() {
return UriComponentsBuilder.fromUri(baseUri())
.path(this.endpointProperties.getRuleSets())
.build()
.toUri();
}
}
What am i missing? Any inputs appreciated.
If you have configured your test environment properly to run MockRestServiceServer
(by that, I mean #RunWith(SpringRunner.class) and #RestClientTest(ClassUnderTestThatCallsTheMockServer.class)), make sure that you are not instantiating your mock server with = new MockServer(), instead just use an instance that is #Autowired from the spring context (because that instance is configured out of the box).
I see that you have a lot of inheritance and overridden methods in your tests, calling things with this.returnSomething..., so make sure that you are not instantiating things outside of the spring context.
Here is a simple example of a mock server to get some posts:
#RunWith(SpringRunner.class)
#RestClientTest(PostClient.class)
public class PostClientMockTest {
// class under test
#Autowired
private PostClient postClient;
// autowired mock server from the spring context
#Autowired
private MockRestServiceServer mockRestServiceServer;
#Test
public void readPosts() throws Exception {
String mockJsonResponse = "My response";
mockRestServiceServer.expect(requestTo("https://myurl.com/posts?userId=1"))
.andRespond(withSuccess(mockJsonResponse, MediaType.APPLICATION_JSON_UTF8));
List<Post> posts = postClient.readPosts(1);
assertEquals(9, posts.size());
mockRestServiceServer.verify();
}
}
Hope this helps

How to camel route test spring bean with mockito mocked bean

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.

Resources