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

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

Related

Hystrix Feign Retry for Timeout not working

I have a Feign Configuration and Hystrix Commands in my project.
below is Feign Config
#Configuration
public class FeignRetryConfig {
#Primary
#Bean
public Feign.Builder feignBuilder(Retryer nephosFeignRetryer) {
return HystrixFeign.builder()
.errorDecoder(new FeignErrorDecoder())
.retryer(nephosFeignRetryer);
}
// retry set to 3 times
#Bean
public Retryer nephosFeignRetryer() {
return new Retryer.Default(10, SECONDS.toMillis(5), 5);
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
and below is my ErrorDecoder:
public class FeignErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
#Override
public Exception decode(String methodKey, Response response) {
Exception exception = defaultErrorDecoder.decode(methodKey, response);
if (response.status() == 500) {
log.error(String.format("##### Got %s response from %s #######", response.status(),
methodKey));
return new RetryableException(
exception.getMessage(),
exception,
null
);
}
return exception;
}
}
and below is my client:
#FeignClient(name = "TEST-CONFIG", configuration = FeignRetryConfig.class, fallbackFactory =
XYZClientFallbackFactory.class)
public interface TestClient {
#RequestMapping(value = "/test", method = RequestMethod.GET, consumes =
MediaType.APPLICATION_JSON_VALUE)
Observable<String> test();
}
SO from TEST-CONFIG I am throwing IOException ( 500 Error ) to Test, but it does not make any retry. below is my error:
com.netflix.hystrix.exception.HystrixRuntimeException: TestClient#test() failed and fallback failed.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:815)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:790)
at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1451)
at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1376)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: feign.RetryableException: status 500 reading TestClient#test(); content:
{"status":500,"erroritems":[{"code":"RuntimeException","message":"org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection"}]}
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:301)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:297)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
... 30 common frames omitted
Caused by: feign.FeignException: status 500 reading TestClient#test(); content:
{"status":500,"erroritems":[{"code":"RuntimeException","message":"org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection"}]}
at feign.FeignException.errorStatus(FeignException.java:62)
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91)
Can Somebody Help Please, What am I Missing ?
I guess you have hystrix enabled. Try setting
feign.hystrix.enabled: false
and see if it works then; if so it would prove your configuration to be ok. There is a post on hystrix and retrying that suggests that this does not go well together. If you want to keep hystrix enabled (and why should you not), perhaps it is worth looking at spring-retry to circumvent the problem.

mocking when multiple RestTemplate

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.

Unit Testing - Wiremock verify failing with connection error

I'm testing a spring-boot application and using wiremock stubs to mock external API. In one test case I want to make sure that my stub gets called exactly once but it's failing with connection error.
My Test File:
#SpringBootTest
#AutoConfigureWebTestClient
#ActiveProfiles("test")
class ControllerTest {
#Autowired
private lateinit var webClient: WebTestClient
private lateinit var wireMockServer: WireMockServer
#BeforeEach
fun setup() {
wireMockServer = WireMockServer(8081)
wireMockServer.start()
setupStub()
}
#AfterEach
fun teardown() {
wireMockServer.stop()
}
// Stub for external API
private fun setupStub() {
wireMockServer.stubFor(
WireMock.delete(WireMock.urlEqualTo("/externalApiUrl"))
.willReturn(
WireMock.aResponse()
.withHeader("Content-Type", "application/json")
.withStatus(204)
.withBodyFile("file.json")
)
)
}
#Test
fun test_1() {
val email = "some-email"
val Id = 123
webClient.post()
.uri { builder ->
builder.path("/applicationUrl")
.queryParam("email", email)
.queryParam("id", Id)
.build()
}
.exchange()
.expectStatus().isOk
WireMock.verify(exactly(1), WireMock.deleteRequestedFor(WireMock.urlEqualTo("/externalApiUrl")))
}
When I run this test I'm getting the following error:
org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused)
Please let me know where I'm doing wrong. Thanks in advance.
You need to perform the verify call on your specific server with something like wireMockServer.verify() instead of WireMock.verify().

How to run Integration Tests with Spring in parallel with dynamic DB ports

Description
We have a growing application with numerous integration tests which involve connection to a redis DB. Because of the growing numbers we want to parallelize them at least at class level.
Till now we did run all tests sequentially and started (stopped) an embedded redis DB (com.github.kstyrc embedded-redis 0.6) in the static #BefroreClass/#AfterClass methods (jUnit 4).
The port of the DB is always the same -- 9736. This is also set in the application.properties via spring.redis.port=9736 for our jedis connection pool.
For the parallelization to work we have to get our port dynamically as well as announce it to the connection factory for connection pooling.
This problem I got solved after some time by implementing BeanPostProcessor in a configuration. The remaining issue I have is with the correct interception of the bean lifecycle and the web application context.
Code snippets parallel testing
application.properties
...
spring.redis.port=${random.int[4000,5000]}
...
The BeanPostProcessor implementing config
#Configuration
public class TestConfig implements BeanPostProcessor {
private RedisServer redisServer;
private int redisPort;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (JedisConnectionFactory.class.equals(bean.getClass())) {
redisPort = ((JedisConnectionFactory) bean).getPort();
redisServer().start();
}
return bean;
}
#Bean(destroyMethod = "stop")
public RedisServer redisServer() {
redisServer = RedisServer.builder().port(redisPort).build();
return redisServer;
}
}
Startup and shutdown for parallel testing with dynamic port
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class OfferControllerTest {
private MockMvc mockMvc;
#Inject
protected WebApplicationContext wac;
...
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(this.wac).apply(springSecurity()).build();
}
#After
public void tearDown() throws Exception {
offerRepository.deleteAll();
}
...
Test parallelization is achieved trough maven-surefire-plugin 2.18.1
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<parallel>classes</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>
Supplement
What happens is, that during springs bean inititialization phase our TestConfig hooks into the lifecycle of the JedisConnectionFactory bean and starts a redis server on the random choosen port through spring.redis.port=${random.int[4000,5000]} before the connection pool is initiated. Since the redisServer itself is a bean we use the destroyMethod to stop the server on bean destruction and therefore leaving this to the application context lifecycle.
The transition from sequential to parallel went well regarding static port to dynamic port.
Problem
But when I run the tests in parallel I get errors like these:
java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#22b19d79 has been closed already through
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(this.wac).apply(springSecurity()).build();
}
and
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.redis-org.springframework.boot.autoconfigure.data.redis.RedisProperties': Initialization of bean failed; nested exception is java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#22b19d79 has been closed already through
#After
public void tearDown() throws Exception {
offerRepository.deleteAll();
}
Help
I am not really sure about the problem. Maybe we can ommit the tearDown call to offerRepository.deleteAll()
because of #DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
but the error at setup webAppContextSetup(this.wac).apply(springSecurity()).build() would still remain.
Did the application contexts get screwed when running in parallel or why is the application context in setup already been closed?
Did we choose the wrong approach (wrong pattern)? If so, what should we change?

Spring Boot Testing: exception in REST controller

I have a Spring Boot application and want to cover my REST controllers by integration test.
Here is my controller:
#RestController
#RequestMapping("/tools/port-scan")
public class PortScanController {
private final PortScanService service;
public PortScanController(final PortScanService portScanService) {
service = portScanService;
}
#GetMapping("")
public final PortScanInfo getInfo(
#RequestParam("address") final String address,
#RequestParam(name = "port") final int port)
throws InetAddressException, IOException {
return service.scanPort(address, port);
}
}
In one of test cases I want to test that endpoint throws an exception in some circumstances. Here is my test class:
#RunWith(SpringRunner.class)
#WebMvcTest(PortScanController.class)
public class PortScanControllerIT {
#Autowired
private MockMvc mvc;
private static final String PORT_SCAN_URL = "/tools/port-scan";
#Test
public void testLocalAddress() throws Exception {
mvc.perform(get(PORT_SCAN_URL).param("address", "192.168.1.100").param("port", "53")).andExpect(status().isInternalServerError());
}
}
What is the best way to do that? Current implementation doesn't handle InetAddressException which is thrown from PortScanController.getInfo() and when I start test, I receive and error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.handytools.webapi.exceptions.InetAddressException: Site local IP is not supported
It is not possible to specify expected exception in #Test annotation since original InetAddressException is wrapped with NestedServletException.
Spring Boot Test package comes with AssertJ that has very convenient way of verifying thrown exceptions.
To verify cause:
#Test
public void shouldThrowException() {
assertThatThrownBy(() -> methodThrowingException()).hasCause(InetAddressException .class);
}
There are also few more methods that you may be interested in. I suggest having a look in docs.
In order to test the wrapped exception (i.e., InetAddressException), you can create a JUnit Rule using ExpectedException class and then set the expectMessage() (received from NestedServletException's getMessage(), which contains the actual cause), you can refer the below code for the same:
#Rule
public ExpectedException inetAddressExceptionRule = ExpectedException.none();
#Test
public void testLocalAddress() {
//Set the message exactly as returned by NestedServletException
inetAddressExceptionRule.expectMessage("Request processing failed; nested exception is com.handytools.webapi.exceptions.InetAddressException: Site local IP is not supported");
//or you can check below for actual cause
inetAddressExceptionRule.expectCause(org.hamcrest.Matchers.any(InetAddressException.class))
//code for throwing InetAddressException here (wrapped by Spring's NestedServletException)
}
You can refer the ExpectedException API here:
http://junit.org/junit4/javadoc/4.12/org/junit/rules/ExpectedException.html
You could define an exception handler
#ExceptionHandler(InetAddressException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public Response handledInvalidAddressException(InetAddressException e)
{
log e
return getValidationErrorResponse(e);
}
and then in your test you could do
mvc.perform(get(PORT_SCAN_URL)
.param("address", "192.168.1.100")
.param("port", "53"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.response").exists())
.andExpect(jsonPath("$.response.code", is(400)))
.andExpect(jsonPath("$.response.errors[0].message", is("Site local IP is not supported")));
I had the same issue and i fix it with org.assertj.core.api.Assertions.assertThatExceptionOfType :
#Test
public void shouldThrowInetAddressException() {
assertThatExceptionOfType(InetAddressException.class)
.isThrownBy(() -> get(PORT_SCAN_URL).param("address", "192.168.1.100").param("port", "53"));
}
I hope it's help you !

Resources