The Controller that I'm trying to write unit test case for is as follows -
#RequestMapping("${rest.base.path}/plugin")
public class Controller {
.
.
.
}
The Unit test case is setup -
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
#Autowired
private MockMvc mvc;
#InjectMocks
Controller dataController;
#Mock
PluginService pluginService;
#Test
public void createFiles() throws Exception {
this.mvc = MockMvcBuilders.standaloneSetup(dataController).build();
mvc.perform(MockMvcRequestBuilders.get("/dc/plugin")
.contentType(MediaType.APPLICATION_JSON));
}
On running the unit test, it's unable to resolve the placeholder ${rest.base.path} as I'm not loading the Spring Context. I tried setting the System.setProperty("rest.base.path", "/api") without any success. Is there anyway I can assign value to that placeholder without removing #RunWith(MockitoJUnitRunner.class)?
The key here is to fill placeholder yourself calling StandaloneMockMvcBuilder.addPlaceholderValue
As the documentation states:
In a standalone setup there is no support for placeholder values embedded in request mappings. This method allows manually provided placeholder values so they can be resolved.
So, the following simple snippet should work for you
public class TestController {
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(new Controller()).addPlaceHolderValue("rest.base.path", "dc")
.setControllerAdvice(new ExceptionMapper())
.setMessageConverters(new MappingJackson2HttpMessageConverter(new ExtendedObjectMapper())).build();
}
#Test
public void testGet() throws Exception {
mockMvc.perform(get("/dc/plugin").accept(MediaType.APPLICATION_JSON_VALUE)).andExpect(status().isOk());
}}
For sure, you can achieve the same autowiring your controller.
Related
I write a Spring Boot app and I was able to access and test Controller with MockMvc. The issue is that during testing security is not enforced and I can access Controller with no user.
Am I doing anything wrong? Is it intended behavior?
ControllerTest class:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
private MockMvc mockMvc;
#Mock
private Service service;
#InjectMocks
private Controller controller;
private final static String URL = "/test";
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void test() throws Exception {
mockMvc.perform(get(URL))
.andExpect(status().isOk());
}
}
My SecurityConfig StackOverflow QA.
Your examples uses a plain unit test to test your controller. In this setup the Controller is created by Mockito (the controller field is annotated with Mockito's #InjectMocks).
Mockito is not aware of Spring, in consequence no Spring Security will be setup in your test.
You need to use the SpringRunner to run your test. This runner is Spring aware and allows you to properly initialize your controller before the test is run.
The test should look something like this (junit5):
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = Controller.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service serviceMock;
#Test
public void test() throws Exception {
mockMvc.perform(get(URL))
.andExpect(status().isOk());
}
}
check our the Spring documentation or some tutorials for further information
https://spring.io/guides/gs/testing-web/
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html
I have a custom reader with an #BeforeStep function in order to initialize some data. These data are comming from an external database.
#Component
public class CustomReader implements ItemReader<SomeDTO> {
private RestApiService restApiService;
private SomeDTO someDTO;
#BeforeStep
private void initialize() {
someDTO = restApiService.getData();
}
#Override
public SomeDTO read() {
...
return someDTO
}
}
In my unit test i need to mock the calls to the external database.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = NedBatchApplication.class)
public class CustomReaderTest {
#Autowired
CustomReader customReader;
#Mock
RestApiService restApiService;
#Before
private void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(customReader, "restApiService", restApiService);
Mockito.when(restApiService.getData().thenReturn(expectedData);
}
}
The problem i am facing is the #BeforeStep is executed before the #Before from the unit test, when i lauch my Test. So restApiService.getData() returns null instead of expectedData.
Is there a way to achieve what i want or do i need to do it with a different approach ?
After some reflexion with a co-worker he gave me a solution :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = NedBatchApplication.class)
public class CustomReaderTest {
CustomReader customReader;
#Mock
RestApiService restApiService;
#Before
private void setup() {
MockitoAnnotations.initMocks(this);
Mockito.when(restApiService.getData().thenReturn(expectedData);
this.customReader = new CustomReader(restApiService);
}
#Test
public void test() {
customReader.initialize();
(...)
}
}
Are you certain that the BeforeStep is running before the Before annotation (by using logging or similar?).
It's possible your Mockito invocation is not fully correct. Try using Mockito.doReturn(expectedData).when(restApiService).getData() instead.
As an alternative approach, if the RestApiService was autowired in your custom reader, you'd be able to use the #InjectMocks annotation on the custom reader declaration in your test, which would cause the mocked version of your restApiService to be injected to the class during the test.
Usually when using Spring based tests, try to make dependencies like restApiService (the ones you would like to mock) to be spring beans, and then you can instruct spring to create mock and inject into application context during the application context creation with the help of #MockBean annotation:
import org.springframework.boot.test.mock.mockito.MockBean;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = NedBatchApplication.class)
public class CustomReaderTest {
#MockBean
private RestApiService restApiService;
}
I try to test a #RestController within a integration test suite using MockMvc.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class WebControllerIT {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}
The #RestController (WebController) calls an injected #Service (RestClientService) which uses RestTemplate to call another REST server. This leads to the following error when running the test.
org.springframework.web.client.ResourceAccessException: I/O error on
GET request for "http://test123.com/42/status": test123.com; nested
exception is java.net.UnknownHostException: test123.com
I used MockRestServiceServer for the integration test of the #Service itself but have no idea how to archieve this within the test of #RestController.
How can I simulate a correct REST call of the RestTemplate?
The #RestController class.
#RestController
public class WebController {
private final RestClientService service;
#Autowired
public WebController(RestClientService service) {this.service = service;}
#GetMapping("/status")
public String getStatus() {
// extract pid from database ...
int pid = 42;
return this.service.getStatus(42);
}
}
The #Serviceclass.
#Service
public class RestClientService {
private final RestTemplate restTemplate;
public RestClientService(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
}
Integration/Unit testing doesn't work that way.Objective of this kind of testing is to run through your code and make sure all the business requirement are met but not to hit other system or DB.Here in your case u shouldn't be hitting test123.com to get back data.What needs to done here is that you should mock that method.
public String getStatus(int pid) {
String url = String.format("http://test123.com/%d/status", pid);
return this.restTemplate.getForObject(url, String.class);
}
So that control doesn't enter this method but return you back the mock data(Dummy data).
For example let say that there are 2 kind of status this method is returning and you need to do some business validation based on the string returned.In this case u need to write 2 integration test and make sure the mocking method returns 2 different value(Dummy value instead of hitting that end point)
Reason why we are writing unit testing/integration testing is to make sure your entire code is working as expected but not to hit other system from ur code.
If you want to only test your controller layer, you would do like this.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MockServletContext.class)
#WebAppConfiguration
public class WebControllerIT {
private MockMvc mockMvc;
private RestClientService service
#Mock
private RestTemplate restTemplate
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
service = new RestClientService(restTemplate);
WebController webController = new WebController(service);
mvc = MockMvcBuilders.standaloneSetup(webController).build();
}
#Test
public void getStatusReurnsSomething() throws Exception {
//Mock the behaviour of restTemplate.
doReturn("someString").when(restTemplate).getForObject(anyString(), anyString());
this.mockMvc.perform(get("/status")).andExpect(status().isOk());
}
}
I never did a test and decided I would do my first.
I am using Spring Boot and by default the layout is as follows: https://zapodaj.net/c8b65f1644e95.png.html
The file that is created on startup looks like this
#RunWith(SpringRunner.class)
#SpringBootTest
public class RestWebServicesApplicationTests {
#Test
public void contextLoads() {
}
}
I do not change anything here, because I do not need it.
By contrast, I created a new class
public class CheckUserDataRestControllerTest {
#Mock
private UserService userService;
#InjectMocks
private CheckUserDataRestController checkUserDataRestController;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(checkUserDataRestController).build();
}
#Test
public void textExistsUsername() throws Exception {
mockMvc
.perform(get("/checkUsernameAtRegistering")
.param("username", "jonki97"))
.andExpect(status().isOk());
}
}
To test the controller method, which should return the status OK.
#GetMapping("/checkUsernameAtRegistering")
public HttpEntity<Boolean> checkUsernameAtRegistering(#RequestParam String username) {
return ResponseEntity.ok().body(!userService.existsByUsername(username));
}
However, during the test, he throws me out
java.lang.AssertionError: Status
Expected :200
Actual :404
<Click to see difference>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
...
This is the tested controller: https://pastebin.com/MWW9YwiH
I did not make any special configurations because I did not find out that I needed to do something.
The error 404 means the Mock does not recognize your endpoint to test. The end point should be:
#Test
public void textExistsUsername() throws Exception {
mockMvc
.perform(get("/checkUserData/checkUsernameAtRegistering")
.param("username", "jonki97"))
.andExpect(status().isOk());
}
Hope this help.
I have a spring-boot application which exposes a REST interface via a controller. This is an example of my controller:
#RestController
public class Controller {
#Autowired
private Processor processor;
#RequestMapping("/magic")
public void handleRequest() {
// process the POST request
processor.process();
}
}
I am trying to write unit tests for this class and I have to mock the processor (since the processing takes very long time and I am trying to avoid this step during testing the controller behavior). Please note, that the provided example is simplified for the sake of this question.
I am trying to use the mockito framework for this task:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
#Test
public void testControllerEmptyBody() throws Exception {
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
}
}
However, this fails with
java.lang.IllegalArgumentException: Could not find field [processor] of type [null] on target [class org.company.Controller]
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:112)
...
Could please someone give me a hint, how this mock could be injected in my controller?
Shouldn't you be passing an instance to set the field on, rather than the class, e.g.:
...
#Autowired
private Controller controller;
...
#Before
public void setUp() throws Exception {
...
Processor processor = Mockito.mock(Processor.class);
ReflectionTestUtils.setField(controller, "processor", processor);
}
I think that you can inject directly the mock like:
#InjectMocks
private ProcessorImpl processorMock;
And remove this line:
ReflectionTestUtils.setField(Controller.class, "processor", processor);
See Injection of a mock object into an object to be tested declared as a field in the test does not work using Mockito?
Rework your controller to use constructor injection instead of field injection. This makes the dependency explicit and makes your test setup drastically simpler.
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Processor processor = Mockito.mock(Processor.class);
//This line should be added to perform mock for Processor.
Mockito.when(processor.process()).thenReturn(<Your returned value>);
//ReflectionTestUtils.setField(Controller.class, "processor", processor);
}
In above put the your returned value for "Your returned value" and in test use this value to verify your output.
You can remove servlet context class in SpringApplicationConfiguration and mock servlet context. There is no need for injection of WebApplicationContext and ReflectionTestUtils.
Basically, your code should look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
#ActiveProfiles("test")
public class ControllerTest {
#InjectMocks
private MyController controller;
#Mock
private Processor processor;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testControllerEmptyBody() throws Exception {
when(proessor.process()).thenReturn(<yourValue>);
this.mockMvc.perform(post("/magic")).andExpect(status().isOk());
verify(processor, times(<number of times>)).process();
}
}
Processor will be mocked and mock will be injected into controller.