Spring Boot: How to inject server.contextPath from application.properties into MockServletContext - spring-boot

In application.properties I configured server.contextPath=/app-service.
Unfortunately, this values is not present in the test context:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = { Application.class, SwaggerConfig.class })
#WebAppConfiguration
public class Swagger2MarkupTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
this.context.getServletContext().getContextPath(); // null
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
}
I need the contextPath for the Swagger Docket which uses per default
springfox.documentation.spring.web.paths.RelativePathProvider which determines the contextPath by
#Override
protected String applicationPath() {
return isNullOrEmpty(servletContext.getContextPath()) ? ROOT : servletContext.getContextPath();
}
It is all about the RelativePathProvider which gets called much earlier than the unit test. It is not about injecting the contextPath into the unit test itself since it is to late as the RelativePathProvider already called servletContext.getContextPath() for the SwaggerConfiguration.

You can achive that in several ways, here are two of them:
Inject spring Environment and get the property value:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = { Application.class, SwaggerConfig.class })
#WebAppConfiguration
public class Swagger2MarkupTest {
#Autowired
private WebApplicationContext context;
#Autowired
private Environment environment;
private MockMvc mockMvc;
#Before
public void setUp() {
environment.getProperty("server.contextPath"); // should return /app-service
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
}
Inject it using #Value:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = { Application.class, SwaggerConfig.class })
#WebAppConfiguration
public class Swagger2MarkupTest {
#Autowired
private WebApplicationContext context;
#Value("server.contextPath")
private String contextPath; // should populate with /app-service
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
}

Related

Spring Boot Unit Testing MockMvc Null Body

I am having dificulties with using MockMvc.
Here I have simple Service and controller classes:
Service:
#Slf4j
#Service
public class EmployeeService {
//...
public Employee GetSample() {
//...
//filling Employee Entities
return new Employee(
"Harriet"
, "random"
, 25);
}
}
controller:
#RestController
#RequestMapping(value = "/info")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#Validated
public class EmployeeController {
private final EmployeeService employeeService;
#PostMapping("/GetEmployee")
public ResponseEntity<Employee> GetEmployee() {
Employee employee = employeeService.GetSample();
return new ResponseEntity<>(employee, HttpStatus.OK);
}
}
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class EmployeeTestCase {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController EmployeeController;
#Mock
private EmployeeService employeeService;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build();
}
#Test
public void getEmployee() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.post("/info/GetEmployee")).andDo(print());
}
}
when I try to use MockMvc I get null body. It seems employee is null. But I didn't understand why.
I thought that when test uses perform, it should initialise employee and later on it should't be null.
I tried to cut the code as much as possible. I hope it is not long.
Note : also tried to use Mockito.when(employeeController.GetEmployee()).thenCallRealMethod();
The #SpringBootTest annotation loads the complete Spring application
context. That means you do not mock your layers
(Services/Controllers).
If you wanted to test specific layers of your application, you could look into test slice annotations offered by Springboot: https://docs.spring.io/spring-boot/docs/current/reference/html/test-auto-configuration.html
In contrast, a test slice annotation only loads beans required to test a particular layer. And because of this, we can avoid unnecessary mocking and side effects.
An example of a Test Slice is #WebMvcTest
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = HelloController.class,
excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
}
)
public class HelloControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void hello() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
#Test
public void helloDto() throws Exception {
String name = "hello";
int amount = 1000;
mvc.perform(
get("/hello/dto")
.param("name", name)
.param("amount", String.valueOf(amount)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.amount", is(amount)));
}
}
However if you really wanted to load up the SpringBoot Application context, say for an Integration Test, then you have a few options:
#SpringBootTest
#AutoConfigureMockMvc
public class TestingWebApplicationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class AuctionControllerIntTest {
#Autowired
AuctionController controller;
#Autowired
ObjectMapper mapper;
MockMvc mockMvc;
#Before
public void setUp() throws Exception {
System.out.println("setup()...");
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void create_ValidAuction_ShouldAddNewAuction() throws Exception {
final Auction auction = new Auction(
"Standing Desk",
"Stand up desk to help you stretch your legs during the day.",
"Johnnie34",
350.00);
mockMvc.perform(post("/auctions")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(auction)))
.andExpect(status().isCreated());
}
}
Lets say you don't want to load up any layers at all, and you want to mock everything, such as for example a Unit Test:
#RunWith(MockitoJUnitRunner.class)
class DemoApplicationTest {
#Mock
private UserRepository userRepository;
private Demo noneAutoWiredDemoInstance;
#Test
public void testConstructorCreation() {
MockitoAnnotations.initMocks(this);
Mockito.when(userRepository.count()).thenReturn(0L);
noneAutoWiredDemoInstance = new Demo(userRepository);
Assertions.assertEquals("Count: 0", noneAutoWiredDemoInstance.toString());
}
}

JUnit test for RestController with JDBCTemplate in SpringBoot

My Controller is something like below :
#Autowired
JdbcTemplate jdbcTemplate;
#RequestMapping(value="/1",method=RequestMethod.POST)
public ResponseEntity<List<Map<String, Object>>> abc(#RequestBody String[] arr) {
List<Map<String, Object>> result =jdbcTemplate.queryForList("select * from Table_Name where COL1='"+arr[1]+"' and COL2='"+arr[2]+"' order by COL3");
return new ResponseEntity<List<Map<String,Object>>>(result,HttpStatus.OK);
}
I wrote the test class as below :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MainClass.class)
#WebAppConfiguration
public class TestWebApplication {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testabc() throws Exception{
mvc.perform(post("/1").andExpect(status().isOk()).andExpect(content().contentType("application/json;charset=UTF-8")));
}
Getting error at andExpect, asking to cast that method.
Please anyone help me out in writing Junit test case for JDBC template.
Thanks In Advance.
Updated the test class to below :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MainClass.class)
#WebAppConfiguration
public class TestWebApplication {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testabc() throws Exception{
String[] arr ={"A","B"};
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
String requestJson=ow.writeValueAsString(arr );
mockMvc.perform( post("/1").contentType( MediaType.APPLICATION_JSON).content(requestJson));
}

How to inject MockMvc with multiple objects in Spring Boot REST Junit test case

I have a SpringBoot REST API connecting to Oracle DB.
My controller calls BusinessImpl layer and in-turn BusinessImpl calls multiple DAO layers (Controller -> BusinessImpl -> DAO1, DAO2, DAO3)
The below test case works perfectly
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration()
#TestPropertySource("classpath:dev-manifest.yml")
#ContextConfiguration(classes = Application.class)
#ConfigurationProperties(prefix = "env")
#SpringBootTest
public class MyTest
{
private static final String REQUEST_URI = "/v1/registration/accounts/links";
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setup()
{
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testSave()
{
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
But I do not want to hit real database during junit test case. So I wrote a mock.
Working Code
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
SomeRequest someRequest = new SomeRequest();
someRequest.setStatus("SUCCESS");
SomeResponse status = registrationBusinessImpl.createUser(SomeRequest, "127.0.0.1");
}
The above code worked perfectly. In businessImpl class securityAuditDAO.getState returned "somestring". But when I introduced mockMvc.perform it stopped working.
Not Working
#Test
public void testSave()
{
when(securityAuditDAO.getState(Mockito.any())).thenReturn("somestring");
String testInput = "some json input";
mockMvc.perform(post(REQUEST_URI).content(testInput).contentType(contentType))
.andExpect(status().isOk());
}
The above code was still hitting the database. So I realized that I should inject mockMvc with securityAuditDAO so I added the following line
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
Code
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Mock
private SecurityAuditDAO securityAuditDAO;
#InjectMocks
private RegistrationBusinessImpl registrationBusinessImpl;
#InjectMocks
RegistrationApiController registrationApiController;
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
//this.mockMvc = webAppContextSetup(webApplicationContext).build();
this.mockMvc = MockMvcBuilders.standaloneSetup(securityAuditDAO).build();
//this.mockMvc = MockMvcBuilders.standaloneSetup(registrationApiController).build();
//ReflectionTestUtils.setField(mockMvc, "securityAuditDAO", securityAuditDAO);
}
I tried injecting securityAuditDAO. But if I do that, my other autowired instances in BusinessImpl were null.
How to inject securityAuditDAO without affecting others or how to inject both webApplicationContext and securityAuditDAO.
Also tried ReflectionTestUtils.setField but it didn't work as expected.

Spring injection: #MockBean #Repository is not injected

I'm trying to #MockBean a #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {}
I'm injecting it into a #Service annotated class:
#Service
public class AuthorizationService {
private ApplicationDao appsDao;
private List<Application> allowedApplications;
#Autowired
public AuthorizationService(ApplicationDao appsDao) {
this.appsDao = appsDao; //<<MOCKED INJECTED BEAN>>
this.fillApplications();
}
private void fillApplications() {
this.appsDao.findAll() //<<MOCKED method>>
.forEach(entry -> {
this.allowedApplications.put(entry.getName(), entry);
});
}
public bool isAuthorized(Application application) {
return this.allowedApplications
.stream()
.anyMatch(app -> app.getId().equals(application.getId()));
}
}
My test mocking configuration looks like:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class GroupReferencesTest {
private #Autowired AuthorizationService;
private #MockBean ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app)); //<<FAILS>>
}
}
Nevertheless, my mocked object is not injected. I mean, when my AuthorizationService calls its injected ApplicationDao is returns an empty list instead of my mocked list.
I've tried to use #MockBean(name="applicationDao") as well. The behavior is the same.
I've also tried to configure my mocked bean using this code:
#TestConfiguration
public class RestTemplateTestConfiguration {
#Bean("applicationDao")
#Primary
public static ApplicationDao mockApplicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(mock.findAll())
.thenReturn(allowedApplications);
return mock;
}
}
However, it doesn't works right.
Application class is:
public class Application {
private String id;
//setters & getters
}
Any ideas?
First things first - the type of test. Answer: Unit test.
You are starting Spring context that manages a lifecycle of AuthorizationService and then you are trying to inject mock. What really happens is that Spring IoC container is injecting a real ApplicationDao (the one managed by Spring IoC container) into the AuthorizationService.
Solution:
Manage lifecyle of AuthorizationService by your test runner (like MockitoJUnitRunner and inject ApplicationDao mock into it):
#RunWith(MockitoJUnitRunner.class)
public class GroupReferencesTest {
private #InjectMocks AuthorizationService authorizationService;
private #Mock ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app));
}
}
Working example
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {AuthorizationService.class})
public class GroupReferencesTest {
#Autowired
private AuthorizationService;
#MockBean
private ApplicationDao applicationDao;
#Test
public void test() {
//given
Mockito.when(applicationDao.findAll()).thenReturn(emptyList());
//when & then
assertTrue(authorizationService.isAuthorized(app));
}
}

How to mock a class that been annotated with Primary

I have :
an interface : EntityService
a first implementation : EntityServiceImpl - This class is annotated with #Primary
an other one : EntityServiceClientImpl
and a controller that has this field #Autowired EntityService
I would like to do a test on this controller and for this test to be unitary I mock EntityService.
So of course this code does not work because Spring detects two beans annotated with Primary :
#Configuration
class EntityControllerTestConfig {
#Bean
#Primary
EntityService entityService() {
return mock(EntityService.class);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#WebAppConfiguration
#ContextConfiguration(classes = EntityControllerTestConfig.class)
public class EntityControllerTest {
#Autowired
private EntityService entityService;
...
#SpringBootApplication(scanBasePackages= "com.company.app")
#EntityScan (basePackages = {"com.company.app" }, basePackageClasses = {Jsr310JpaConverters.class })
#EnableJpaRepositories("com.company.app")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
I tried to find an other way to mock and to exclude EntityServiceClient on test configuration but i was not able to mock. (cf : exclude #Component from #ComponentScan )
I finaly found that solution : a spring context (with controller, controllerAdvice and mock of service) and not a spring boot context
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class EntityControllerTest {
#Configuration
public static class EntityControllerTestConfig {
#Bean
public EntityService entityService() {
return mock(EntityService.class);
}
#Bean
public EntityController entityController() {
return new EntityController(entityService());
}
}
#Autowired
private EntityService entityService;
#Autowired
private EntityController entityController;
private MockMvc mockMvc;
#Before
public void setup() throws DomaineException {
this.mockMvc = MockMvcBuilders
.standaloneSetup(entityController)
.setControllerAdvice(new myControllerAdvice())
.build();
NB : Since Spring 4.2 you can set your ControllerAdvice like that.
You can approach it slightly differently and combine #WebMvcTest with #MockBean annotation to test just the controller with it's own minimal context.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = EntityController.class)
public class EntityControllerTest {
#MockBean
private EntityService entityService;
#Autowired
private MockMvc mvc;
In this example EntityService will be mocked while MockMvc can be used to assert the request mappings in controller.

Resources