I am trying to do integration tests which include the execution of an ApplicationRunner.
I use #SpringBootTest, and when the context is initialized, the ApplicationRunner is automatically launched, which is okay.
What I want to achieve is to inject a Mock in the ApplicationRunner AND configure that mock BEFORE the ApplicationRunner is executed.
It doesn't work if I configure the mock IN the test, because by the time the test is executed, the Spring context has already been initialized and the ApplicationRunner has already been executed.
Is there a way to configure the mock after the Spring context has been initialized, and before the ApplicationRunner is executed?
If you have a bean dependency for your ApplicationRunner class, You can mock it as below.
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationRunnerTest {
#MockBean
private final Dependency dependency;
#Autowired
private final ApplicationRunner applicationRunner;
#Before
public void setUp() throws Exception {
}
#Test
public void testMethod() {
}
}
Hope this answers your question
Here webApplicationContext is initialised whenever we try to execute a test case as #Before get initiated which calls setUp() of AbstractTest class which has initialisation logic of webApplicationContext
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public abstract class AbstractTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
public class UserControllerTest extends AbstractTest {
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void testGet() throws Exception {
String uri = “/url”;
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
}
}
Main Spring boot class
#SpringBootApplication
public class Main extends SpringBootServletInitializer{
private Logger LOGGER = (Logger) LoggerFactory.getLogger(FactsMain.class);
#Value("${facts.trustCertPath}")
private String trustCertPath;
public static void main(String args[]) {
SpringApplication.run(Main.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(FactsMain.class);
}
#PostConstruct
public void setSSLPath() {
LOGGER.info("trustCertPath - " + trustCertPath);
System.setProperty("javax.net.ssl.trustStore", trustCertPath);
}
}
Related
I am trying to do a JUnit test to test a Controller.
My Controller consists of:
#Autowired
private OrderManager orderManager;
#RequestMapping(value = "/getOrderList", method = RequestMethod.POST)
#ResponseBody
public Map<String, Object> getOrderTables(OrderSearchDto orderSearchDto) {
...
orderHdrTables = orderManager.findAllOrders(orderSearchDto);
...
}
and my test is:
#RunWith(MockitoJUnitRunner.class)
public class FilterActivityTest {
#InjectMocks
private OrderHdrController orderHdrController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(orderHdrController).build();
}
#Test
public void testActivityFilter() throws Exception {
OrderSearchDto orderSearchDto = new OrderSearchDto();
OrderSearchPanelDto orderSearchPanelDto = new OrderSearchPanelDto();
List <String> activityTypes = Arrays.asList("DELIVERY","START_UPLOAD");
orderSearchPanelDto.setActivityTypes(activityTypes);
orderSearchDto.setOrderSearchPanelDto(orderSearchPanelDto);
mockMvc.perform(MockMvcRequestBuilders.post("/orders/getOrderList")
.content(asJsonString(orderSearchDto))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.data[0].tripNo", is("SG1606LLR08469")))
.andExpect(jsonPath("$.data.data[1].tripNo", is("SG1606LLR08470")));;
}
private static String asJsonString(final Object obj) {
try {
final ObjectMapper mapper = new ObjectMapper();
final String jsonContent = mapper.writeValueAsString(obj);
return jsonContent;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
But during debug of the test, I am getting orderManager is null in my Controller. What can I do to initialise it?
I have modified your test a bit. Please try this.
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
FilterActivityTest.Config.class,
OrderHdrController.class,
})
public class FilterActivityTest {
#Configuration
static class Config {
#Bean
OrderManager orderManager() {
return mock(OrderManager.class);
}
}
#Autowired
private OrderHdrController orderHdrController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(orderHdrController).build();
//remember to mock orderManager.findAllOrders here
}
}
You need to initialize the mocks
Update your #BeforeEach method like this:
#Mock
private OrderManager orderManager;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(orderHdrController).build();
}
Also, you may not need #RunWith(MockitoJUnitRunner.class)
Love Spring Testing Even More With Mocking and Unit Test Assistant:
A mocked service replaces multiple dependencies
enter image description here
#Controller
#RequestMapping("/people")
public class PeopleController {
#Autowired
protected PersonService personService;
#GetMapping
public ModelAndView people(Model model) {
for (Person person: personService.getAllPeople()) {
model.addAttribute(person.getName(), person.getAge());
}
return new ModelAndView("people.jsp", model.asMap());
}
}
private MockMvc mockMvc:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
private MockMvc mockMvc;
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
I get a mistake when I want to run mockMvc
java.lang.NullPointerException
Perform the following steps:
create service mock instead of service original
("PersonServiceMock")
replace service original by service mock
#Autowired
PersonService personService;
#Autowired
PeopleController peopleController;
private MockMvc mockMvc;
#Before
public void setup() {
peopleController = new PeopleController(new personServiceMock());
mvc = MockMvcBuilders.standaloneSetup(peopleController).build();
}
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
That's because you are never initialising mockMvc in your code and the point where you access it results in nullPointerException. You need to initialise it before using it, and since multiple tests in your class could be using it, best place to do it is setup() method annotated with #before. Try below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
from the source code I see that the mockMvc doesn't have any value, thats why it hits "java.lang.NullPointerException" for this line of code :
ResultActions actions = mockMvc.perform(get("/people"));
to make it run, I think need to give value to mockMvc first.
by constructor :
#Test
public void testPeople() throws Exception {
mockMvc = new MockMvc();
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
or Autowired :
#Autowired
MockMvc mockMvc
depends on the purpose of MockMvc Class
we are trying to do an intergration test our interceptors in our spring boot application using spring boot version 1.4.0, but not sure how; here is our application setting
#Configuration
#EnableAutoConfiguration()
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilderconfigure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
we then customed out webmvc by extending WebMvcConfigurerAdapter
#Configuration
public class CustomServletContext extends WebMvcConfigurerAdapter{
#Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(testInterceptor).addPathPatterns("/testapi/**");
}
}
so we wanna to test the interceptor, but we don't wanna really start the application, cause there are many dependency beans that need to read a externally defined property files to construct
we have tried the following
#SpringBootTest(classes = CustomServletContext.class)
#RunWith(SpringRunner.class)
public class CustomServletContextTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void interceptor_request_all() throws Exception {
RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping) applicationContext
.getBean("requestMappingHandlerMapping");
assertNotNull(mapping);
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/test");
HandlerExecutionChain chain = mapping.getHandler(request);
Optional<TestInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(chain.getInterceptors()))
.filter(TestInterceptor.class).first();
assertTrue(containsHandler.isPresent());
}
}
but it alters org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'requestMappingHandlerMapping' is defined
Do we need to create a bean of requestMappingHandlerMapping to test the interceptors? is there any magical way to do this in spring boot ?
You can create a test like this :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { MyIncludedConfig.class })
#ActiveProfiles("my_enabled_profile")
public class BfmSecurityInterceptorTest2 {
public static final String TEST_URI = "/test";
public static final String RESPONSE = "test";
// this way you can provide any beans missing due to limiting the application configuration scope
#MockBean
private DataSource dataSource;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testInterceptor_Session_cookie_present_Authorized() throws Exception {
ResponseEntity<String> responseEntity = testRestTemplate.getForEntity(TEST_URI, String.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isEqualTo(RESPONSE);
}
#SpringBootApplication
#RestController
public static class TestApplication {
#GetMapping(TEST_URI)
public String test() {
return RESPONSE;
}
}
}
Notes
Interceptors only work if you set SpringBootTest.WebEnvironment.RANDOM_PORT
You have to provide enough configuration so your interceptors are executed
To speed up the test you can exclude not wanted beans and configurations, see examples
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertTrue;
#SpringBootTest()
class LoggingInterceptorConfigurationTest {
#Autowired
private RequestMappingHandlerMapping mapping;
#Test
public void LoggingInterceptorShouldBeApplied() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/example");
HandlerExecutionChain chain = mapping.getHandler(request);
assert chain != null;
Optional<HandlerInterceptor> LoggingInterceptor = chain.getInterceptorList()
.stream()
.filter(LoggingInterceptor.class::isInstance)
.findFirst();
assertTrue(LoggingInterceptor.isPresent());
}
#Test
public void LoggingInterceptorShouldNotBeAppliedToHealthURL() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/health");
HandlerExecutionChain chain = mapping.getHandler(request);
assert chain != null;
Optional<HandlerInterceptor> LoggingInterceptor = chain.getInterceptorList()
.stream()
.filter(LoggingInterceptor.class::isInstance)
.findFirst();
assertTrue(LoggingInterceptor.isEmpty());
}
}
I am trying to write a test for this Java SpringBoot's class:
https://github.com/callistaenterprise/blog-microservices/blob/master/microservices/composite/product-composite-service/src/main/java/se/callista/microservices/composite/product/service/ProductCompositeIntegration.java
Specifically, I am trying to "mock" this method call:
URI uri = util.getServiceUrl("product");
I figured out I should "mock" the ServiceUtils object in order to do this. I tried this using the #Mock and #InjectMocks annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
public class ProductCompositeIntegrationTest {
#InjectMocks
#Autowired
private ProductCompositeIntegration productIntegration;
#Autowired
private RestTemplate restTemplate;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void myTest() {
Mockito.when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
ResponseEntity<Iterable<Product>> products = productIntegration.getAllProducts();
}
}
But this way it still calls the original ServiceUtils object, and not the "mocked" one. Also tried without the #Autowired annotation at the ProductCompositeIntegration, but this results in a NullPointerException.
What am I doing wrong?
My main class looks like this:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
public class ProductCompositeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductCompositeServiceApplication.class, args);
}
}
The ServiceUtils object that I am trying to mock is specified in a class, annotated with Spring's #Component annotation to inject it into the other classes using #Autowired.
After a lot of trial and error I managed to solve this problem.
I dropped the
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
annotations aboved the test class.
I marked the class that I was testing with #InjectMocks and the dependencies with #Mock:
public class ProductCompositeIntegrationTest {
#InjectMocks
private ProductCompositeIntegration productIntegration;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
private RestTemplate restTemplate = new RestTemplate();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
productIntegration.setRestTemplate(restTemplate);
}
#Test
public void someTests() {
when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
//Test code...
}
}
I'm not sure if this is the best approach ("the Spring way"), but this worked for me.
This article made it all clear to me: http://rdafbn.blogspot.be/2014/01/testing-spring-components-with-mockito.html
You have to write a FactoryBean like
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
In your test-context.xml you have to add the following lines.
<bean id="serviceUtilMock" class="MockitoFactoryBean">
<constructor-arg value="your.package.ServiceUtil" />
</bean>
If you don't use XML configuration, then you have to add the equivalent to above in your Java configuration.
I have following controller code for which I have to write JUnit test case.
public class EquipmentController {
private Map<String, Equipment> equiList = new HashMap <String,Equipment>();
#RequestMapping("/rest/equipment/{Number}")
public Equipment getEquipment(#PathVariable String Number){
if(!equiList.containsKey(Number)){
lNumber = DEFAULT;
}
return equiList.get(Number);
}
}
I'm writing the JUnit test case for the same as below:
import static org.springframework.test.web.ModelAndViewAssert.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({/* include live config here
e.g. "file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml" */})
public class EquipmentControllerTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
private EquipmentController controller;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
// Get the controller from the context here
controller = new EquipmentController();
}
#Test
public void testgetEquipment() throws Exception {
request.getUriString()("lNumber");
final Equipment equip = handlerAdapter.handle(request, response,
controller);
assertViewName(equip, "view");
}
}
But am not sure if this test class is correct or not as I am new to JUnit.
Can anyone please suggest how to do this.
Create a mock of your controller and use MockMvc to test your methods:
import static org.springframework.test.web.ModelAndViewAssert.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({/* include live config here
e.g. "file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml" */})
public class EquipmentControllerTest {
private MockMvc mockMvc;
private EquipmentController controller;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(equipmentController).build()
}
#Test
public void testgetEquipment() throws Exception {
this.mockMvc.perform(get("/rest/equipment/{Number}", 3))
.andExpect(status().isOk())
}
}
where "3" represents value of your path variable.