Unit Testing - Wiremock verify failing with connection error - spring-boot

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().

Related

Unit Testing on Springboot WebClient with MockWebServer

After searching around the net for few days, couldn't really find the resources I need for my use case as many of it are implemented in Java.
I have a service class that is calling external api, and I am trying to write unit tests on it with MockWebServer
#Service
class ApiService {
#Autowired
private lateinit var webClient: WebClient
fun getApiResult(name: String): ResultDto? {
return try {
webClient
.get()
.uri("https://www.example.com/")
.retrieve()
.bodyToMono(Result::class)
.block()
catch {
null
}
}
}
#ExtendWith(MockitoExtension::class)
#MockitoSettings(strictname = Strictness.LENIENT)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ApiServiceTest {
#Mock
private lateinit var webClient: WebClient
#InjectMocks
private lateinit var mockApiService: ApiService
private val mockName = "mockName"
private val mockDetailDto = DetailDto(name = "mockName", age = 18)
#Test
fun canGetDetails() {
mockWebServer.enqueue(
MockResponse()
.setResponse(200)
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody(mockDetailDto))
)
mockApiService.getApiResult(mockName)
val request: RecordedRequest = mockWebServer.takeRequest()
// Assert statements with request
}
}
However, I am getting lateinit property webClient has not been initialised. But if I were to use #Spy instead of #Mock for the webClient in my test, it will be making actual apis calls which is not the goal of unit test. My goal would be able to check the request for some of the details such as GET and its return value. Appreciate the community's help.
By my opinion you not add some configuration, for resolve it automatically you can try make Integration Test for this. Like this
#WithMockUser//if you use spring secutity
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = RANDOM_PORT)
class MyTest {
#Autowired
private MockMvc mvc;
}
and make a test application for quickly loading environment
#SpringBootApplication
#ConfigurationPropertiesScan
#ComponentScan(lazyInit = true)//make loading is faster
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
This way can load full context configuration like prod mode, of course only for called methods if you used lazyInit = true

Idiomatic way of configuring integration tests in spring

What is the idiomatic (and easiest) way of configuring integration tests in Spring?
For example let's define a sample integration test:
#ActiveProfiles("dev", "test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleTest() {
#LocalServerPort
lateinit var port: Number
lateinit var client: WebTestClient
#BeforeEach
fun setup() {
client = WebTestClient.bindToServer().baseUrl("http://localhost:$port").build()
}
#Test
fun test() {
client.get()
.uri("/api/something")
.exchange()
.expectStatus().is2xxSuccessful
.expectBody()
}
}
There are many ways i can configure WebTestClient but I don't know which one is "the best". To name a few:
lateinit var and BeforeEach
configure in each test separately
Given my project's structure and conventions most of things we get from DI is injected via constructor using #Autowired annotation. Is it also possible in this case? Therefore i'd just do:
#ActiveProfiles("dev", "test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleTest(
#Autowired val client: WebTestClient
) {
#Test
fun test() {
client.get()
.uri("/api/something")
.exchange()
.expectStatus().is2xxSuccessful
.expectBody()
}
}
I've tried to do it without other modifications and some of my tests fail because of connection refused

How to specify a specfic port number for Spring WebTestClient

I have a locally running rest endpoint I am attempting to communicate with using Spring WebClient. As a first step for testing purposes, I am trying to use Spring WebTestClient. My local rest endpoint runs on a specific port (lets say 8068). My assumption is that since the port is fixed, I am supposed to use:
SpringBootTest.WebEnvironment.DEFINED_PORT
, and then somehow specify what that port is in my code. However I do not know how to do that. It would appear to default to 8080. Here are the important parts of my code:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SpringWebclientApplicationTests {
#Autowired
private WebTestClient webTestClient;
#Test
public void wcTest() throws Exception {
String fullUri = "/services/myEndpoint/v1?userIds=jsmith&objectType=DEFAULT";
WebTestClient.ResponseSpec responseSpec1 = webTestClient.get().uri(fullUri, "").exchange().expectStatus().isOk();
}
This test expects to return "200 OK", but returns "404 NOT_FOUND". The request shown in the error response is:
GET http://localhost:8080/services/myEndpoint/v1?userIds=jsmith&objectType=DEFAULT
, obviously because it defaults to 8080, and I need to it to be 8068. I woould be grateful to anyone who can explain to define the port properly. Thanks.
I figured it out. I don't believe you are supposed to use
SpringBootTest.WebEnvironment.DEFINED_PORT
unless the endpoint is listening on 8080. In my case, since I needed to use a port number I could not control, i did this instead:
#RunWith(SpringRunner.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SpringWebclientApplicationTests {
private WebTestClient client;
#Before
public void setup() {
String baseUri = "http://localhost:" + "8079";
this.client = WebTestClient.bindToServer().baseUrl(baseUri).build();
}
#Test
public void wcTest() throws Exception {
String fullUri = "/services/myEndpoint/v1?userIds=jsmith&objectType=DEFAULT";
WebTestClient.ResponseSpec responseSpec1 = client.get().uri(fullUri, "").exchange().expectStatus().isOk();
}
}
, where I instantiate the webtestclient locally using the bindToServer method, and not as an autowired bean, and removing the:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
, so it works fine now.

WebTestClient with MockWebServer hangs

I have a spring boot application that exposes a rest api using "spring-boot-starter-webflux".
My application calls other rest services when one of my endpoint is called.
I'm trying to make an integration test using WebTestClient to mimic the client calling me and MockWebServer to mimic the external rest service I call.
I'm also overriding a spring configuration to use a webclient that "points" to the MockWebServer in my application.
I see that, when I launch the test, my server starts, and the MockWebServer starts, but when I call my endpoint it seems that the MockWebServer is "stuck" and is not responding.
The test, moreover, ends with "java.lang.IllegalStateException: Timeout on blocking read for 5000 MILLISECONDS from the WebTestClient.
#SpringBootTest(webEnvironment = RANDOM_PORT,
properties = { "spring.main.allow-bean-definition-overriding=true" })
class ApplicationIT {
static MockWebServer remoteServer = new MockWebServer();
#BeforeAll
static void setUp() throws Throwable {
remoteServer.start();
}
#AfterAll
static void tearDown() throws Throwable {
remoteServer.shutdown();
}
#Test
void test(#Autowired WebTestClient client) {
remoteServer.enqueue(new MockResponse().setBody("..."));
client.get()
.uri("...")
.exchange()
.expectStatus()
.isOk();
}
#TestConfiguration
static class SpringConfiguration {
WebClient remoteClient() {
return WebClient.builder()
.baseUrl("http://" + remoteServer.getHostName() + ":" + remoteServer.getPort())
.build();
}
}
}
Are you sure that your integration tests uses the WebClient bean you specified inside your #TestConfiguration?
It seems there is an #Bean annotation missing:
#TestConfiguration
static class SpringConfiguration {
#Bean
WebClient remoteClient() {
return WebClient.builder()
.baseUrl("http://" + remoteServer.getHostName() + ":" + remoteServer.getPort())
.build();
}
}

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

Resources