Testing Server Sent Event With Rest Assured Framework - spring-boot

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

Related

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

how to create RestController in test directory for SpringBoot Application

Im currently writing integration test for SpringBoot Application .
It's functionality is to receive/send request from outside and forward/receive them to another application(APP_2). So there are two systems which needs to be mocked outside System and APP_2 .
HomeController
#Controller
public class HomeController {
#Autowired
ForwardController forwardController;
#RequestMapping("/")
public #ResponseBody
String greeting() {
return forwardController.processGET().toString();
}
}
ForwardController
#Service
public class ForwardController {
#Autowired
private RestTemplate restTemplate;
#Autowired
private Environment environment;
private ResponseEntity sendRequest(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables) {
return restTemplate.exchange( url, method, requestEntity, responseType,uriVariables);
}
public ResponseEntity processGET()
{
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<?> entity = new HttpEntity<>(headers);
String app_2_url = environment.getProperty(Constants.APP_2_URL);
ResponseEntity<String> response = sendRequest(app_2_url,HttpMethod.GET,entity,String.class);
return response;
}
}
APP_2_CONTROLLER
#Controller
public class App_2_Controller {
#RequestMapping("/app2Stub")
public #ResponseBody
String greeting() {
return "Hello End of world";
}
}
Test Class which simulates the external request behavior to the system:
HTTP_request_Test
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = Application.class)
public class HttpRequestTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private Environment environment;
#Test
public void greetingShouldReturnDefaultMessage() throws Exception {
assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/",
String.class)).contains("Hello End of world");
}
}
Here in this test class I'm overriding the properties by having two property file. So when we run test the request would be sent to App_2_Controller ( Mock in my project ) rather than the real App .
QUESTION :
Is there any way to have the APP_2_CONTROLLER inside the test folder ? This is because I don't want to expose the unwanted test endpoint in my Actual application .
Here in the above project , Im changing the URL with properties. Is there a better way to put a controller for the same URL. For simplicity sake lets assume, app_2 url is app.com:9000/serve
Spring already comes with a MockRestServiceServer, that makes this a lot easier so that you don't have to create your own dummy controllers (App_2_Controller). So in your case, you can remove that controller, and write a test like this for ForwardController:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class ForwardControllerTest {
#Autowired
private RestTemplate restTemplate;
#Autowired
private ForwardController forwardController; // Your service
private MockRestServiceServer server;
#Before
public void setUp() {
server = MockRestServiceServer.bindTo(restTemplate).build();
}
#Test
public void processGet_returnsResponseFromAPI() {
server.expect(once(), requestTo("http://app.com:9000/serve"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("Hello End of world", MediaType.TEXT_PLAIN));
assertThat(forwardController.processGET().getBody()).isEqualTo("Hello End of world"));
}
}
Additionally, you can create a separate test for your actual controller (ForwardController is just a service), mock ForwardController and use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest
public class HomeControllerTest {
#Autowired
private HomeController homeController;
#Autowired
private MockMvc mockMvc;
#MockBean
private ForwardController forwardController;
#Test
public void greeting_usesForwardController() {
when(forwardController.expectGET()).thenReturn("Hello End of world");
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello End of world")));
}
}
In this case, you'll end up with two tests:
One test to verify that RestTemplate is used to capture the proper response from your external REST API.
Another test to verify that HomeController just forwards whatever ForwardController responds.

JUnit not working in Spring Boot with #Autowired annotation

I have a Spring Boot application in which I'm creating REST web services
using the MVC pattern.
I have a controller, service and DAO class and I use the #Autowired annotation for calling methods of the service and DAO layer.
When I create JUnit tests using mockito, the values are going into the controller but from the controller they are not going to the service class.
Here is the code sample:
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1" + result.getResponse()
.getContentAsString());
}`
Please help me with this.
It is not clear from your question what you are trying to mock.
Anyway, you should not be able to debug your service/dao that is mocked since what actually executed in the test is the mocked one and not yours.
If you want to test your controller, you can mock your service or dao and define what the response they will return, and then verify that the response you get from your controller is as you expect it to be.
#EnableWebMvc
#SpringBootApplication(scanBasePackages = { "com.yourPackage.external" })
public class YourApplication extends org.springframework.boot.web.support.SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
Based on what you have pasted, you can do below things:
if you are using #RunWith(SpringJUnit4ClassRunner.class) [better change to #RunWith(SpringRunner.class)] then use
#MockBean
private MyService externalService;
OR
use #RunWith(MockitoJUnitRunner.class) and
#MockBean
private MyService externalService;
#InjectMocks
private MyController controller = new MyController(externalService);
for details checkout :- testing web in spring boot
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController ;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1"+ result.getResponse()
.getContentAsString());
}
}

test spring 5 reactive rest service

I'm using SpringBoot 2 and Spring 5 (RC1) to expose reactive REST services. but I can't manage to write unit test for those controllers.
Here is my controller
#Api
#RestController
#RequestMapping("/")
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(path = "/", method = RequestMethod.GET)
public Flux<MyModel> getPages(#RequestParam(value = "id", required = false) String id,
#RequestParam(value = "name", required = false) String name) throws Exception {
return myService.getMyModels(id, name);
}
}
myService is calling a database so I would like not to call the real one. (I don't wan't integration testing)
Edit :
I found a way that could match my need but I can't make it work :
#Before
public void setup() {
client = WebTestClient.bindToController(MyController.class).build();
}
#Test
public void getPages() throws Exception {
client.get().uri("/").exchange().expectStatus().isOk();
}
But I'm getting 404, seems it can't find my controller
You have to pass actual controller instance to bindToController method.
As you want to test mock environment, you'll need to mock your dependencies, for example using Mockito.
public class MyControllerReactiveTest {
private WebTestClient client;
#Before
public void setup() {
client = WebTestClient
.bindToController(new MyController(new MyService()))
.build();
}
#Test
public void getPages() throws Exception {
client.get()
.uri("/")
.exchange()
.expectStatus().isOk();
}
}
More test examples you can find here.
Also, I suggest switching to constructor-based DI.

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