I am writing integration test case which is used to create and update a data
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApplication.class, webEnvironment =
SpringBootTest.WebEnvironment.DEFINED_PORT)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyIntegrationTest {
private String baseUrl="http://192.168.6.177/api/v1/";
#Autowired
TestRestTemplate restTemplate;
Long createdId; // trying to set ID which is coming after test1_AddData
#Test
public void test1_AddData() throws Exception {
ABC abc = new ABC("Name");
HttpEntity<ABC> requestBodyData = new HttpEntity<>(ABC);
ParameterizedTypeReference<RestTemplateResponseEnvelope<ABC>> typeReference =
new ParameterizedTypeReference<RestTemplateResponseEnvelope<ABC>>() {
};
ResponseEntity<RestTemplateResponseEnvelope<ABC>> response = restTemplate.exchange(
baseUrl + "/presenceType",
HttpMethod.POST, requestBodyData, typeReference);
Assert.assertTrue(HttpStatus.CREATED.equals(response.getStatusCode()));
createdId = response.getBody().getData().getId();
}
#Test
public void test2_updateData() throws Exception {
ABC abc = new ABC("NEW NAME");
System.out.println("------------------------------------------" + createdId); /// it is giving null
HttpEntity<ABC> requestBodyData = new HttpEntity<>(ABC);
ResponseEntity<ABC> response = restTemplate.exchange(
baseUrl + "/presenceType/" + createdId,
HttpMethod.PUT, requestBodyData, ABC.class);
Assert.assertTrue(HttpStatus.OK.equals(response.getStatusCode()));
createdId = response.getBody().getId();
}
}
the output of my execution
------------------------------------------null
What needs to be done to make this execution i.e calling of second function after the exection of first.
NOTE : The code also contains delete method which needs to be called after third.
Although it is not good practice to fix order in Tests. But If you are using JUnit above version 4.11, it has annotation #FixMethodOrder.
You can set order by method names.
Example:
import org.junit.runners.MethodSorters;
import org.junit.FixMethodOrder;
import org.junit.Test;
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderTest {
#Test
public void test1() {
System.out.println("test1");
}
#Test
public void test2() {
System.out.println("test2");
}
}
For further reading #FixMethodOder
Junit Git Page: https://github.com/junit-team/junit4/wiki/test-execution-order
Custom Implemenation: https://memorynotfound.com/run-junit-tests-method-order/
FixMethodOrder not working for me, so I find another way to do this.
We can use #TestMethodOrder from Junit jupiter
https://junit.org/junit5/docs/5.5.0/api/org/junit/jupiter/api/TestMethodOrder.html
#Order annotation used to specify the sequence
Example code:
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DummyTest1 {
#Test
#Order(2)
public void read(){
System.out.println("read");
}
#Test
#Order(4)
public void delete(){
System.out.println("delete");
}
#Test
#Order(1)
public void create(){
System.out.println("create");
}
#Test
#Order(3)
public void update(){
System.out.println("update");
}
}
It will execute in sequence order order1, order2, order3, order4
So testing order will be,
create
read
update
delete
Related
I'm using Junit5 and Spring for test.
I want to initalize spring bean for each test because I don't want different tests to change the other results of tests.
I'm knowing that a new instance of the test class is created before running each test method by default. under result of test codes is true,because the instance variable number is initalized for each test by junit5.
public class TestInstanceVaribale{
int number = 0;
#Test
public void test1() {
number += 3;
Assertions.assertEquals(3, number);
}
#Test
public void test2() {
number += 5;
Assertions.assertEquals(5, number);
}
}
but, this code is failed because spring bean is not initalized.
#Component
public class Car {
public String name = "myCar";
}
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
#SpringBootTest
#TestMethodOrder(OrderAnnotation.class)
public class TestSpringVariable {
#Autowired
Car car;
#Test
#Order(1)
public void test1() {
car.name = "testCar";
Assertions.assertEquals("testCar", car.name);
}
#Test
#Order(2)
public void test2() {
// this is fail. expected: <myCar> but was: <testCar>
// but I want expected: <myCar> but was: <myCar>
Assertions.assertEquals("myCar", car.name);
}
}
How to initalize spring bean for separation between tests in junit?
#SpringBootTest
#initalizeSpringBeanPerMethod <-- I want like this
public class TestSpringVariable2 {
#Autowired
Car car;
#BeforeEach
public void initalize() { <-- I want like this
SpirngBean.initalize()
}
}
Take a look at DirtiesContext
Probably adding this to your class should work. It's telling Spring to reset it's state after/before (depending on how you set it) each test
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
I'm using Java 8 Spring boot. I have below method.
public hello() {
try {
// send message
}
catch(HttpClientErrorException e) {
if (e.getRawStatusCode() == 401) {
// I need to retry the same hello() method for three times as in 10sec, 20sec and 25sec.
}
}
}
I need to call the same method three times for retrying whenever it hits the catch block.
How can I do this asynchronously?
I found below code but it didn't work.
#Retryable( value = {RestClientException.class}, maxAttempts = 3, backoff = #Backoff(3000))
Appreciate your help.
You can use #Async annotation from Spring to achieve that.
You have to create a config like this:
#Configuration
#EnableRetry
#EnableAsync
class RetryConfig {}
When you want to use Async with Retry you have to decorate the method with Async which is trying to call a Retryable method. Also, you have to make sure that you are returning Future<> or similar because you are sending that piece of code for a toss in the background
I have also implemented fallback mechanism otherwise the request will terminate with 500 exception.
If you run the code below you can see that the main request is executed on thread http-nio-8080-exec-1 while your Async code is executed on a different thread task-1.
I tried to explain this with a sample service method, but the concept will be same for local or remote service call.
A detailed exmaple is given below:
package com.example.silentsudo.springcloudssamples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
#SpringBootApplication
public class SpringCloudsSamplesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudsSamplesApplication.class, args);
}
}
#RequestMapping(path = "sample")
#RestController
class SampleController {
private final GreetService greetService;
SampleController(GreetService greetService) {
this.greetService = greetService;
}
#GetMapping
public String hello() {
System.out.println(Thread.currentThread().getName());
return "Hello!";
}
#GetMapping(path = "greet")
public String greet(#RequestParam(value = "name", defaultValue = "John") String name) {
return greetService.greet(name);
}
#Async
#GetMapping(path = "greet-async")
public CompletableFuture<String> greetAsync(#RequestParam(value = "name", defaultValue = "John") String name) {
return CompletableFuture.completedFuture(greetService.greet(name));
}
}
#Configuration
#EnableRetry
#EnableAsync
class RetryConfig {
}
#Service
class GreetService {
private final UngaBungaService ungaBungaService;
GreetService(UngaBungaService ungaBungaService) {
this.ungaBungaService = ungaBungaService;
}
#Retryable(maxAttempts = 5, value = GreetException.class, backoff = #Backoff(value = 3000L))
public String greet(String name) {
return ungaBungaService.lol(name);
}
#Recover
public String recoverGreetException(GreetException greetException) {
return greetException.getMessage();
}
}
#Service
class UngaBungaService {
public String lol(String name) {
System.out.println(Thread.currentThread().getName());
throw new GreetException("Called greet for " + name);
}
}
class GreetException extends RuntimeException {
public GreetException(String message) {
super(message);
}
}
For retry mechanisms, you can to use the #Retryable(value = RestClientException.class)
For this to trigger, you need to actually throw this exception (or something that extends from RestClientException). Because of your catch statement, no exception is actually thrown, so the retry mechanism doesn't kick in.
#Retryable( value = {RestClientException.class}, maxAttempts = 3, backoff = #Backoff(3000))
public void hello() {
try {
// send message
}
catch(HttpClientErrorException e) {
if (e.getRawStatusCode() == 401) {
throw new RestClientException("meaningfull message");
}
}
}
If you want to run some catch code after the 3 retries failed, you can make use of the #Recover annotation on a recovery method.
If you want some more info on the retry mechanism, you could look here
Also don't forget to add #EnableRetry in your config so that the annotations are used.
Full code example with spring boot
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
context.getBean(TestService.class).hello();
context.close();
}
#Configuration
#EnableRetry
public class AppConfig {
}
#Service
public class TestService {
#Retryable(value = {IllegalArgumentException.class}, maxAttempts = 4, backoff = #Backoff(delay = 1000, multiplier = 4))
public void hello() {
try {
int a = Integer.parseInt(null);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Exception triggered");
}
}
}
I have a requirement to create an auto-configuration for service call on spring-boot startup.
i.e., During spring-boot startup, the below service has to be called.
#PostMapping(path = "/addProduct", produces = "application/json", consumes = "application/json")
public #ResponseBody String addProduct(#RequestBody String productStr) {
..<My code>..
}
The add product requires an input like:
{
"product":"test",
"price":"10"
}
This will internally call a database service.
During startup, the json input provided in the console should be fed to this service.
I have no idea on how to achieve this. Verified a couple of Spring documentation. But those does'nt suit the requirement.
Kindly help in explaining a way or providing a right documentation to achieve this.
One way to do this is by implementing ApplicationRunner like this :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
#Component
public class ApplicationInitializer implements ApplicationRunner {
private ProductController productController;
public ApplicationInitializer(ProductController productController) {
this.productController = productController;
}
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
String response = productController.add(product);
System.out.println(response);
}
}
The run method will be invoked at startup with arguments passed in the command line like this : java -jar yourApp.jar --product="{\"name\":\"test\", \"price\":\"15\"}".
And you need a class to map the json to an object like this :
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
You can also call your Controller using the RestTemplate (or WebClient) if needed :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:8080/products", product, String.class);
System.out.println(response);
}
}
Such requirement can be achieved by using an init() method annotated with #PostConstruct in a bean.
e.g.
#Component
public class Foo {
#PostConstruct
public void init() {
//Call your service
}
}
I am trying to write Junit for one REST Webservice Client which uses RestTemplate.
Below is method call to REST Template:
final ResponseEntity<LogDateVO[]> result = restTemplate.postForEntity(url, LogDateInputVO, LogDateVO[].class);
In My Test Class I am trying some thing like below :
mockServer.expect(requestTo("http://abc.def.pvt:80/testOperations/api/traffic/logs/")).andExpect(method(HttpMethod.POST))
.andRespond(MockRestResponseCreators.withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
I am unable to get the result back and not able to continue with this approach, i need some help in getting this.
My intention is when my restTemplate is called it should check for above expect and return success with some data.
Assuming That class under test has a method which calls postForEntity using RestTemplate. For more info check this
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
#Service
public class SimpleRestService {
#Autowired
private RestTemplate restTemplate;
public String getMessage() {
String result;
String httpResult = restTemplate.getForObject("http://google.com",
String.class);
result = "Message SUCCESS result: " + httpResult;
return result;
}
}
Your Test class should will be like as below:
#ContextConfiguration(locations = {"classpath:aap.xml"})
public class SimpleRestServiceFunctionalTest extends AbstractJUnit4SpringContextTests {
#Autowired
private SimpleRestService simpleRestService;
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testGetMessage() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("SUCCESS"), containsString("resultSuccess")));
}
#Test
public void testGetMessage_500() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withServerError());
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("FAILED"), containsString("500")));
}
#Test
public void testGetMessage_404() {
mockServer.expect(requestTo("http://google.com")).andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.NOT_FOUND));
String result = simpleRestService.getMessage();
mockServer.verify();
assertThat(result, allOf(containsString("FAILED"), containsString("404")));
}
}
I have the following request handler for saving autos. I have verified that this works when I use e.g. cURL. Now I want to unit test the method with Spring MVC Test. I have tried to use the fileUploader, but I am not managing to get it working. Nor do I manage to add the JSON part.
How would I unit test this method with Spring MVC Test? I am not able to find any examples on this.
#RequestMapping(value = "autos", method = RequestMethod.POST)
public ResponseEntity saveAuto(
#RequestPart(value = "data") autoResource,
#RequestParam(value = "files[]", required = false) List<MultipartFile> files) {
// ...
}
I want to uplod a JSON representation for my auto + one or more files.
I will add 100 in bounty to the correct answer!
Since MockMvcRequestBuilders#fileUpload is deprecated, you'll want to use MockMvcRequestBuilders#multipart(String, Object...) which returns a MockMultipartHttpServletRequestBuilder. Then chain a bunch of file(MockMultipartFile) calls.
Here's a working example. Given a #Controller
#Controller
public class NewController {
#RequestMapping(value = "/upload", method = RequestMethod.POST)
#ResponseBody
public String saveAuto(
#RequestPart(value = "json") JsonPojo pojo,
#RequestParam(value = "some-random") String random,
#RequestParam(value = "data", required = false) List<MultipartFile> files) {
System.out.println(random);
System.out.println(pojo.getJson());
for (MultipartFile file : files) {
System.out.println(file.getOriginalFilename());
}
return "success";
}
static class JsonPojo {
private String json;
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
}
}
and a unit test
#WebAppConfiguration
#ContextConfiguration(classes = WebConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class Example {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void test() throws Exception {
MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some xml".getBytes());
MockMultipartFile secondFile = new MockMultipartFile("data", "other-file-name.data", "text/plain", "some other type".getBytes());
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", "{\"json\": \"someValue\"}".getBytes());
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders.multipart("/upload")
.file(firstFile)
.file(secondFile)
.file(jsonFile)
.param("some-random", "4"))
.andExpect(status().is(200))
.andExpect(content().string("success"));
}
}
And the #Configuration class
#Configuration
#ComponentScan({ "test.controllers" })
#EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
The test should pass and give you output of
4 // from param
someValue // from json file
filename.txt // from first file
other-file-name.data // from second file
The thing to note is that you are sending the JSON just like any other multipart file, except with a different content type.
The method MockMvcRequestBuilders.fileUpload is deprecated use MockMvcRequestBuilders.multipart instead.
This is an example:
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;
/**
* Unit test New Controller.
*
*/
#RunWith(SpringRunner.class)
#WebMvcTest(NewController.class)
public class NewControllerTest {
private MockMvc mockMvc;
#Autowired
WebApplicationContext wContext;
#MockBean
private NewController newController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wContext)
.alwaysDo(MockMvcResultHandlers.print())
.build();
}
#Test
public void test() throws Exception {
// Mock Request
MockMultipartFile jsonFile = new MockMultipartFile("test.json", "", "application/json", "{\"key1\": \"value1\"}".getBytes());
// Mock Response
NewControllerResponseDto response = new NewControllerDto();
Mockito.when(newController.postV1(Mockito.any(Integer.class), Mockito.any(MultipartFile.class))).thenReturn(response);
mockMvc.perform(MockMvcRequestBuilders.multipart("/fileUpload")
.file("file", jsonFile.getBytes())
.characterEncoding("UTF-8"))
.andExpect(status().isOk());
}
}
Have a look at this example taken from the spring MVC showcase, this is the link to the source code:
#RunWith(SpringJUnit4ClassRunner.class)
public class FileUploadControllerTests extends AbstractContextControllerTests {
#Test
public void readString() throws Exception {
MockMultipartFile file = new MockMultipartFile("file", "orig", null, "bar".getBytes());
webAppContextSetup(this.wac).build()
.perform(fileUpload("/fileupload").file(file))
.andExpect(model().attribute("message", "File 'orig' uploaded successfully"));
}
}
Here's what worked for me, here I'm attaching a file to my EmailController under test. Also take a look at the postman screenshot on how I'm posting the data.
#WebAppConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = EmailControllerBootApplication.class
)
public class SendEmailTest {
#Autowired
private WebApplicationContext webApplicationContext;
#Test
public void testSend() throws Exception{
String jsonStr = "{\"to\": [\"email.address#domain.com\"],\"subject\": "
+ "\"CDM - Spring Boot email service with attachment\","
+ "\"body\": \"Email body will contain test results, with screenshot\"}";
Resource fileResource = new ClassPathResource(
"screen-shots/HomePage-attachment.png");
assertNotNull(fileResource);
MockMultipartFile firstFile = new MockMultipartFile(
"attachments",fileResource.getFilename(),
MediaType.MULTIPART_FORM_DATA_VALUE,
fileResource.getInputStream());
assertNotNull(firstFile);
MockMvc mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext).build();
mockMvc.perform(MockMvcRequestBuilders
.multipart("/api/v1/email/send")
.file(firstFile)
.param("data", jsonStr))
.andExpect(status().is(200));
}
}
If you are using Spring4/SpringBoot 1.x, then it's worth mentioning that you can add "text" (json) parts as well . This can be done via MockMvcRequestBuilders.fileUpload().file(MockMultipartFile file) (which is needed as method .multipart() is not available in this version):
#Test
public void test() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.fileUpload("/files")
// file-part
.file(makeMultipartFile( "file-part" "some/path/to/file.bin", "application/octet-stream"))
// text part
.file(makeMultipartTextPart("json-part", "{ \"foo\" : \"bar\" }", "application/json"))
.andExpect(status().isOk())));
}
private MockMultipartFile(String requestPartName, String filename,
String contentType, String pathOnClassPath) {
return new MockMultipartFile(requestPartName, filename,
contentType, readResourceFile(pathOnClasspath);
}
// make text-part using MockMultipartFile
private MockMultipartFile makeMultipartTextPart(String requestPartName,
String value, String contentType) throws Exception {
return new MockMultipartFile(requestPartName, "", contentType,
value.getBytes(Charset.forName("UTF-8")));
}
private byte[] readResourceFile(String pathOnClassPath) throws Exception {
return Files.readAllBytes(Paths.get(Thread.currentThread().getContextClassLoader()
.getResource(pathOnClassPath).toUri()));
}
}