Reputation: 733
I have a REST controller in a Spring boot application, simplyfied:
@RestController
@RequestMapping("/api")
public class MyRestController {
@Autowired
private Environment env;
private String property1;
@PostConstruct
private void init() {
this.property1 = env.getProperty("myproperties.property_1");
}
@GetMapping("/mydata")
public String getMyData() {
System.out.println("property1: " + this.property1);
...
}
In application.yml I have defined the property similar to:
myproperties:
property_1: value_1
When I use the REST controller, it works as expected, the value value_1 is read, and in the GET method present.
Now I wanted to test it with a unit test, similar too:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApp.class)
public class MyRestControllerTest {
@Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
@Autowired
private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
@Autowired
private ExceptionTranslator exceptionTranslator;
private MockMvc restMyRestControllerMockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
final MyRestController myRestController = new MyRestController();
this.restMyRestControllerMockMvc = MockMvcBuilders.standaloneSetup(myRestController)
.setCustomArgumentResolvers(pageableArgumentResolver).setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService()).setMessageConverters(jacksonMessageConverter)
.build();
}
@Test
public void getMyDataTest() throws Exception {
restMyRestControllerMockMvc.perform(get("/api/mydata"))
.andExpect(status().isOk());
}
When the method in test is executed, the value of the property property1 is null.
Why is that?
The code above is partially generated by JHipster, I'm not sure if this is a optimal solution, just reused it.
Thanks!
Upvotes: 0
Views: 3953
Reputation: 375
MockMvcBuilders.standaloneSetup not loads SpringContext so properties data are not available. You can verify this by using @Value("${myproperties.property_1}") annotation directly inside MyRestControllerTest - it will return "value_1" value (but inside MyRestController - will return null).
Please change it to MockMvcBuilders.webAppContextSetup and inject WebApplicationContext. (Eventually you can inject Environment bean into MyRestController by it constructor, but in my opinion this is Spring hacking.)
Warning: also remember that (in Maven layout project) application.yml need to be copied to src/test/resources.
Code example:
@RestController
@RequestMapping("/api")
public class MyRestController {
@Autowired
private Environment env;
private String envProperty;
@Value("${myproperties.property_1}")
private String valueProperty;
@PostConstruct
private void init() {
this.envProperty = env.getProperty("myproperties.property_1");
}
@GetMapping("/mydata")
public String getMyData() {
System.out.println("envProperty: " + this.envProperty);
System.out.println("valueProperty: " + this.valueProperty);
return "";
}
@GetMapping("/myproblem")
public String getMyProblem() {
throw new IllegalArgumentException();
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApp.class)
public class MyRestControllerTest {
private MockMvc restMyRestControllerMockMvc;
@Autowired
private WebApplicationContext context;
@Before
public void setup() {
final MyRestController myRestController = new MyRestController();
// this.restMyRestControllerMockMvc = MockMvcBuilders.standaloneSetup(myRestController)
// .build();
this.restMyRestControllerMockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
@Test
public void getMyDataTest() throws Exception {
restMyRestControllerMockMvc.perform(get("/api/mydata"));
}
@Test
public void getMyProblemTest() throws Exception {
restMyRestControllerMockMvc.perform(get("/api/myproblem"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isConflict());
}
}
@ControllerAdvice
public class ControllerAdvicer {
@ResponseStatus(HttpStatus.CONFLICT)
@ExceptionHandler(IllegalArgumentException.class)
public String assertionException(final IllegalArgumentException e) {
return "xxx";
}
}
Upvotes: 1
Reputation: 733
I marked kasopey answer as correct, as it contains a complete answer, although in parts the answers of the other responders are also correct.
But still I would like to know what those line are for:
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService())
.setMessageConverters(jacksonMessageConverter)
because with your solution to use
MockMvcBuilders.webAppContextSetup(context)
those methods are not available. How to achive the same, if necessary?
The missing method in my sample code looks like this:
... Create a FormattingConversionService which use ISO date format, instead of the localized one.
public static FormattingConversionService createFormattingConversionService() {
DefaultFormattingConversionService dfcs = new DefaultFormattingConversionService ();
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(dfcs);
return dfcs;
}
And again, the most part of the code is generated by JHipster, which is quite convenient, but not always clear why and what for this is.
Upvotes: 0
Reputation: 526
Use @Value
annotation to read values from your app.yml
@RestController
@RequestMapping("/api")
public class MyRestController {
@Autowired
private Environment env;
@Value("${myproperties.property_1}")
private String property1;
@GetMapping("/mydata")
public String getMyData() {
System.out.println("property1: " + this.property1);
...
}
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Upvotes: 1