How to use WebTestClient to connect a http server? - spring

As javadoc of WebTestClient described: This client can connect to any server over HTTP.
But code below doesn't really request over http:
#SpringBootTest(webEnvironment = DEFINED_PORT)
#AutoConfigureWebTestClient
public class HelloControllerTest {
#Autowired
WebTestClient webTestClient;
#Test
public void test_hello() {
webTestClient
.get()
.uri("http://localhost:8080/hello/World")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.name").isEqualTo("aaa");
}
#Test
public void test_hello2() {
webTestClient = webTestClient.mutate().baseUrl("http://localhost:8080").build();// even this does not work
webTestClient.get()
.uri("/hello/World")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.name").isEqualTo("aaa");
}
}
please help how to use WebTestClient to connect a http server?

This code works:
WebTestClient webTestClient = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
Thanks for this answer: https://stackoverflow.com/a/63094850/13789176

Related

Testing Server Sent Event With Rest Assured Framework

I am using spring boot and rest assured to test my REST services
I have a rest controller, such as
#GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
Flux<String> getFlux() {
...
How do I assert on the response body correctly?
I think the value comes back as "data: " for a stream that terminates after one output.
I also have no idea how to test a stream actual works when trying to affect the data asynchronously?
Reactive Testing
You should use WebTestClient to test your reactive services.
Here is an example
The service method:
public Flux<GithubRepo> listGithubRepositories(String username, String token) {
return webClient.get()
.uri("/user/repos")
.header("Authorization", "Basic " + Base64Utils
.encodeToString((username + ":" + token).getBytes(UTF_8)))
.retrieve()
.bodyToFlux(GithubRepo.class);
}
The Test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebclientDemoApplicationTests {
#Autowired
private WebTestClient webTestClient;
#Test
public void test2GetAllGithubRepositories() {
webTestClient.get().uri("/api/repos")
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBodyList(GithubRepo.class);
}
}
Please find the examples here https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/
Standard MVC Testing
MockMVC
You could use a mock environment where the controller classes are tested without really launching a servlet container:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MockMvcExampleTests {
#Autowired
private MockMvc mvc;
#Test
public void exampleTest() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isOk())
.andExpect(content().string("Hello World"));
}
}
With a running server:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortTestRestTemplateExampleTests {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void exampleTest() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
Please read more about Spring Boot Testing in the official documentation
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing

Spring Boot Server and Client testing. Is it possible to combine them to achieve something like this?

Combining server testing MockMvc, and client testing #RestClientTest. Is this possible or will they always clash with each other?
#AutoConfigureMockMvc
#RestClientTest(BackendApiClient.class)
public class ApiGatewayControllerTest extends ApiTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private MockRestServiceServer mockServer;
#Test
public void get_backend_defs_should_return_200() throws Exception {
mockServer.expect(ExpectedCount.once(), requestTo("/apirepo"))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(loadFileAsString("json/client/get_all.json")));
getJson("/get/backend").andExpect(isOk()).andExpect(
MockMvcResultMatchers.content().json(loadFileAsString("json/controller/get_all.json")));
mockServer.verify();
}
private ResultActions getJson(String path) throws Exception {
return mockMvc.perform(
MockMvcRequestBuilders.get(path).accept(MediaType.APPLICATION_JSON));
}
}
Thank you

Spring, webflux: The getRemoteAddress method of the ServerHttpRequest object returns null when request performed from WebTestClient

I have a controller
#RestController
public class NameController {
#Autowired
private NameService nameService;
#GetMapping("/name")
public Mono<UploadParamsDto> getName(ServerHttpRequest request) {
return nameService.getNameByHost(request.getRemoteAddress().getHostName());
}
}
and i have a test method:
#ExtendWith(SpringExtension.class)
#WebFluxTest(NameControllerTest.class)
#ActiveProfiles("test")
class NameControllerTest {
#Autowired
private WebTestClient webClient;
#Test
void nameTest() {
webClient.get().uri("/name")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus()
.isOk();
}
}
When I run the test in order to check my getName method i got NPE because
request.getRemoteAddress() returns null, could you please tell me how to mock ServerHttpRequest? (I know that there is MockServerHttpRequest, but I couldn't managed with it)
My purpose is make request.getRemoteAddress().getHostName() return mock value.
Thanks to everyone.
Works in next way:
#ExtendWith(SpringExtension.class)
#WebFluxTest(NameControllerTest.class)
#ActiveProfiles("test")
class NameControllerTest {
#Autowired
private ApplicationContext context;
#Test
void nameTest() {
WebTestClient webClient = WebTestClient
.bindToApplicationContext(context)
.webFilter(new SetRemoteAddressWebFilter("127.0.0.1"))
.configureClient()
.build();
webClient.get().uri("/name")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus()
.isOk();
}
}
Where SetRemoteAddressWebFilter is WebFilter:
public class SetRemoteAddressWebFilter implements WebFilter {
private String host;
public SetRemoteAddressWebFilter(String host) {
this.host = host;
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(decorate(exchange));
}
private ServerWebExchange decorate(ServerWebExchange exchange) {
final ServerHttpRequest decorated = new ServerHttpRequestDecorator(exchange.getRequest()) {
#Override
public InetSocketAddress getRemoteAddress() {
return new InetSocketAddress(host, 80);
}
};
return new ServerWebExchangeDecorator(exchange) {
#Override
public ServerHttpRequest getRequest() {
return decorated;
}
};
}
}
Running a test with #WebFluxTest doesn't involve a real server, you've figured that out.
But getting a NullPointerException doesn't feel right still - could you create an issue on https://jira.spring.io about that? I don't think you should have to work around this, but Spring Framework should probably provide some infrastructure to "mock" that information.

Spring boot - integration testing - WebTestClient & HttpServletRequest

I'm having difficulties figuring this out.
I can mock almost everything but for some reason the HttpServletRequest is mocked but not injected into the #ControllerAdvice #ExceptionHandler method.
Any ideas? Thank you for your help in advance!
STR Repo with minimal plug and play test suite / code
https://github.com/krodyrobi/spring-integration-test-str
#Component
public class Config {
private final String url = "https://httpstat.us/";
public String getUrl() {
return url;
}
}
#RestControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<String> handleException(HttpServletRequest request, WebClientResponseException ex) {
return new ResponseEntity<>(request.getRequestURL() + " " + ex.getResponseBodyAsString(), ex.getStatusCode());
}
}
#RestController
public class SomeController {
private final Config config;
#Autowired
public SomeController(Config config) {
this.config = config;
}
#GetMapping("/test")
public Mono<String> test() {
return WebClient
.create(config.getUrl())
.get()
.uri("/200")
.retrieve()
.bodyToMono(String.class);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {
SomeController.class,
GlobalExceptionHandler.class,
})
public class SomeControllerTest {
private final static String baseUrl = "http://localhost:9999/";
public #Rule WireMockRule wireMockRule = new WireMockRule(9999);
private #MockBean Config config;
private #MockBean HttpServletRequest request;
private WebTestClient webClient;
private #Autowired SomeController controller;
private #Autowired GlobalExceptionHandler exceptionHandler;
#Before
public void setUp() {
webClient = WebTestClient
.bindToController(controller)
.controllerAdvice(exceptionHandler)
.build();
when(config.getUrl()).thenReturn(baseUrl);
}
#Test
public void test_works() {
wireMockRule
.stubFor(get(urlEqualTo("/200"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody("200 MOCK")));
webClient
.get()
.uri("/test")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("200 MOCK");
wireMockRule.verify(getRequestedFor(urlMatching("/200")));
}
#Test
public void test_fails() {
// java.lang.IllegalStateException: No suitable resolver for argument 0
// of type 'javax.servlet.http.HttpServletRequest' on public
// org.springframework.http.ResponseEntity<java.lang.String>
// com.example.demo.GlobalExceptionHandler.handleException(
// javax.servlet.http.HttpServletRequest,
// ...client.WebClientResponseException
// )
wireMockRule
.stubFor(get(urlEqualTo("/200"))
.willReturn(aResponse()
.withStatus(404)
.withHeader("Content-Type", "text/plain")
.withBody("404 MOCK")));
webClient
.get()
.uri("/test")
.exchange()
.expectStatus()
.isNotFound()
.expectBody(String.class)
.isEqualTo("Http://localhost:8080/test 404 MOCK");
wireMockRule.verify(getRequestedFor(urlMatching("/200")));
}
}
Use below instead of HttpServletRequest
import org.springframework.http.server.reactive.ServerHttpRequest;
ServerHttpRequest request

Spring Boot Test MockMvc perform post - Not working

I'm trying to make a Integration test using the Spring Boot but the post request is not working. The method saveClientePessoaFisica is never called and do not return any kind of error! I just tried to make other tests using a get method and it works properly.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("dev")
public class ClienteControllerIT {
#Autowired
private MockMvc mvc;
#Test
public void nao_deve_permitir_salvar_cliente_pf_com_nome_cpf_duplicado() throws Exception {
this.mvc.perform(post("/api/cliente/pessoafisica/post")
.contentType(MediaType.APPLICATION_JSON)
.content("teste")
.andExpect(status().is2xxSuccessful());
}
}
#RestController
#RequestMapping(path = "/api/cliente")
public class ClienteController {
#Autowired
private PessoaFisicaService pessoaFisicaService;
#PostMapping(path = "/pessoafisica/post", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> saveClientePessoaFisica(#RequestBody PessoaFisica pessoaFisica) throws Exception {
this.pessoaFisicaService.save(pessoaFisica);
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
}
Your content "teste" is no valid JSON. When I am using your code, I'm getting a JsonParseException complaining about that (By the way, there is a parenthese missing after content("teste") ). Also helpful is using andDo(print()) which will give you the request and response in more detail:
#Test
public void nao_deve_permitir_salvar_cliente_pf_com_nome_cpf_duplicado() throws Exception {
this.mvc.perform(post("/api/cliente/pessoafisica/post")
.contentType(MediaType.APPLICATION_JSON)
.content("teste"))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
Some things to look for :
Enable logging in your mockmvc
Enable your mockmvc properly
When using spring security, initialise it in mockmvc
When using spring security / CSRF / HTTP POST , pass a csrf in your mockmvc
Enable debug logging
Like this :
logging:
level:
org.springframework.web: DEBUG
org.springframework.security: DEBUG
Working test :
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#AutoConfigureMockMvc
public class MockMvcTest {
#Autowired
protected ObjectMapper objectMapper;
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void init() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).apply(springSecurity()).build();
}
#Test
public void adminCanCreateOrganization() throws Exception {
this.mockMvc.perform(post("/organizations")
.with(user("admin1").roles("ADMIN"))
.with(csrf())
.contentType(APPLICATION_JSON)
.content(organizationPayload("org1"))
.accept(APPLICATION_JSON))
.andDo(print())
.andExpect(status().isCreated());
}
}

Resources