I have a Spring boot code with Aspectj. This code has written with basic MVC architecture. Then I just try to test it with MockMVC. But when I try to test it, Aspectj doesn't interrupted. Is there a special configuration about Aspectj?
Controller:
#GetMapping("user/{userId}/todo-list")
public ResponseEntity<?> getWaitingItems(#RequestUser CurrentUser currentUser){
...handle it with service method.
}
Aspect:
#Pointcut("execution(* *(.., #RequestUser (*), ..))")
void annotatedMethod()
{
}
#Before("annotatedMethod() && #annotation(requestUser)")
public void adviseAnnotatedMethods(JoinPoint joinPoint, RequestUser requestUser)
{
...
}
Test:
#WebMvcTest(value = {Controller.class, Aspect.class})
#ActiveProfiles("test")
#ContextConfiguration(classes = {Controller.class, Aspect.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ControllerTest
{
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Controller controller;
#MockBean
private Service service;
#Before
public void setUp()
{
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void getWaitingItems() throws Exception
{
mockMvc.perform(get("/user/{userId}/todo-list", 1L))
.andExpect(status().isOk());
}
}
There is no need for a #SpringBootTest if you wanna do integration tests of specific controller (web layer) + your custom Aspect logic (AOP layer).
Try something like this
#WebMvcTest(controllers = {AnyController.class})
#Import({AopAutoConfiguration.class, ExceptionAspect.class})
public class ErrorControllerAdviceTest {
AnyController.class: controller under test
AopAutoConfiguration.class: Spring Boot auto-configuration of AOP
ExceptionAspect.class: class containing AOP logic
#Aspect
#Component
public class ExceptionAspect {}
Tested with Spring Boot 2.2.1.RELEASE and JUNIT5.
I am unsure, if my solution is technically the same like #Deadpool answers
Spring #WebMvcTest will only instantiate web layer and it will not load complete application context
However, in this test, Spring Boot instantiates only the web layer rather than the whole context.
In order to test Aspectj you need to load whole application context using #SpringBootTest annotation
The #SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context
So annotate the test using #SpringBootTest annotation
#SpringBootTest
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private Controller controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void getWaitingItems() throws Exception {
mockMvc.perform(get("/user/{userId}/todo-list", 1L))
.andExpect(status().isOk());
}
}
Related
Spring Boot here. I currently have the following REST controller:
#RestController
public class FizzbuzzController {
private final FizzbuzzService FizzbuzzService;
public FizzbuzzController(FizzbuzzService FizzbuzzService) {
this.FizzbuzzService = FizzbuzzService;
}
#PostMapping("/Fizzbuzzs/{fizzbuzzId}")
public ResponseEntity<FizzbuzzDTO> addFizzbuzz(#RequestParam("files") List<MultipartFile> files,
#PathVariable String fizzbuzzId) throws IOException {
FizzbuzzDTO fizzbuzzDTO = fizzbuzzService.store(files, fizzbuzzId);
return ResponseEntity.status(HttpStatus.OK).body(fizzbuzzDTO);
}
}
I would like to write an integration test for it that:
Mocks or stubs an HTTP request to the URL; and
Allows me to inject the FizzbuzzController (under test) with a mocked FizzbuzzService or the real thing; and
Allows me to inspect the HTTP response coming back from the method (check status code, check response entity, etc.)
My best attempt thus far:
#WebMvcTest(FizzbuzzController.class)
#EnableConfigurationProperties
#AutoConfigureMockMvc
public class FizzbuzzControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FizzbuzzService FizzbuzzService;
#Test
public void should_store_fizzbuzz_files() throws Exception {
// I can't even get the test to run
assertTrue(1 == 1);
}
}
When I run this, the test fails to run and it is clear (looking at the logs) that Spring is loading the entire application context of my app, whereas I just want it to isolate the context to this test class, the main FizzbuzzController class, and anything in the dependency tree underneath it.
Can anyone spot where I'm going awry?
You need another context for testing. I'm suggesting you to have a separate Test config:
#TestConfiguration
#Slf4j
#EnableJpaRepositories("tth.patientportal.repository")
public class TestConfig { // bean configs goes here for testing if you need to change
// context}
and in a controller test build the context like below:
#RunWith(SpringRunner.class)
#AutoConfigureTestEntityManager
#SpringBootTest
#TestPropertySource("classpath:application-unittest.properties")
#ContextConfiguration(classes = {TestConfig.class})
public class RestControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup()
{
mockMvc = MockMvcBuilders.
webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void shouldReturnRegisteredUser() throws Exception {
this.mockMvc.
perform(MockMvcRequestBuilders
.post("url")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.username").exists());
}
}
how can I create a "quasi" MVC integration test in Spring Boot. I would like to use my real implementation of a service, but I can't mange to do it. How can I inject real implementation instead of a mock.
My classes look like this
#Controller
#RequiredArgsConstructor
public class DashboardController {
private final RolesManagerService rolesManagerService;
private final ServletRequestManagerService servletRequestManagerService;
#GetMapping({"/", "/dashboard"})
public String index(Model model, HttpServletRequest httpServletRequest) {
model.addAttribute("canAddNewOrder", rolesManagerService.canRoleAccessApplicationPart(servletRequestManagerService.getRole(httpServletRequest), ApplicationPart.CREATE_NEW_ORDER));
model.addAttribute("var", "test");
return "dashboard";
}
}
and the test
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = DashboardController.class)
#AutoConfigureMockMvc
class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserDetailsService userDetailsService;
#MockBean
RolesManagerService rolesManagerService;
#MockBean
private ServletRequestManagerService servletRequestManagerService;
#Test
void testDashboard() throws Exception {
mockMvc.perform(get("/dashboard").with(user("admin").password("pass").roles("USER","ADMIN")))
.andExpect(status().isOk())
.andExpect(view().name("dashboard"))
.andExpect(xpath("//a").nodeCount(1))
.andExpect(model().attributeExists("canAddNewOrder"))
.andExpect(model().size(2))
.andExpect(model().attribute("var", equalTo("test")))
.andExpect(model().attribute("canAddNewOrder", equalTo(false)))
.andDo(print());
}
}
Generally WebMvcTest will not create a full Spring context with all components (Service etc) injected, but only the Controller you define. Either use a full SpringBootTest, or add something like this in your WebMvcTest class:
#TestConfiguration
static class AdditionalTestConfig {
#Bean
public RolesManagerService getService() {
return new RolesManagerService();
}
}
I have this MvcTest in my application:
#SpringBootTest
#WebMvcTest
public class BarsControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testBars() throws Exception {
mockMvc.perform(get("/bars")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
but when I run the test mockMvc is null when running the tests.
You shouldn't use #WebMvcTest and #SpringBootTest together.
If you want to test both web layer and other layers Use #AutoConfigureMockMvc and #SpringBootTest together:
#SpringBootTest
#AutoConfigureMockMvc
public class BarsControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testBars() throws Exception {
mockMvc.perform(get("/bars")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
Or if you only want to test web layer you can use just #WebMvcTest: note the this does not load full spring application context(It only loads web layer)
#WebMvcTest
public class BarsControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testBars() throws Exception {
mockMvc.perform(get("/bars")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
How to write JUnit Test cases for RestController, Service and DAO layer?
I've tried MockMvc
#RunWith(SpringRunner.class)
public class EmployeeControllerTest {
private MockMvc mockMvc;
private static List<Employee> employeeList;
#InjectMocks
EmployeeController employeeController;
#Mock
EmployeeRepository employeeRepository;
#Test
public void testGetAllEmployees() throws Exception {
Mockito.when(employeeRepository.findAll()).thenReturn(employeeList);
assertNotNull(employeeController.getAllEmployees());
mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/employees"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
How can I verify the CRUD methods inside the rest controller and other layers ?
You can use #RunWith(MockitoJUnitRunner.class) for unit testing with your Service Layer mocking your DAO Layer components. You don't need SpringRunner.class for it.
Complete source code
#RunWith(MockitoJUnitRunner.class)
public class GatewayServiceImplTest {
#Mock
private GatewayRepository gatewayRepository;
#InjectMocks
private GatewayServiceImpl gatewayService;
#Test
public void create() {
val gateway = GatewayFactory.create(10);
when(gatewayRepository.save(gateway)).thenReturn(gateway);
gatewayService.create(gateway);
}
}
You can use #DataJpaTest for integration testing with
your DAO Layer
#RunWith(SpringRunner.class)
#DataJpaTest
public class GatewayRepositoryIntegrationTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private GatewayRepository gatewayRepository;
// write test cases here
}
Check this article for getting more details about testing with Spring Boot
I use Spring boot + Spring Security + Spring Actuator
My JUnit test class:
#RunWith(SpringRunner.class)
#SpringBootTest()
#AutoConfigureMockMvc
public class ActuatorTests {
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser(roles={"USER","SUPERUSER"})
public void getHealth() throws Exception {
mockMvc.perform(get("/health"))
.andExpect(status().isOk());
}
}
is OK, but when I set management.port: 8088, my test is KO with this message:
[ERROR] ActuatorTests.getHealth:37 Status expected:<200> but was:<404>
How to set management port in my JUnit test MockMvc or test configuration?
When management.port is different to server.port Spring will create a separate web application context and a dedicated servlet container where it will register all actuators. A default MockMvc routes requests against the main application web context and not the management one. That is what happening in your case - since no actuators are running in the main application web context you get a 404. To test endpoints running in a management context use the following setup:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ManagementContextMvcTest {
#Autowired
private ManagementContextResolver resolver;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(
(WebApplicationContext) resolver.getApplicationContext()).build();
}
#Test
#WithMockUser(roles = { "USER", "SUPERUSER" })
public void getHealth() throws Exception {
mockMvc.perform(get("/health"))
.andExpect(status().isOk());
}
}