Spring #WebMvcTest with Spring Security - java

I have a problem when I want to test controllers using #WebMvcTest - on application load I get:
...
Field customUserDetailsService in com.test.config.SecurityConfig required a bean of type 'com.test.security.CustomUserDetailsService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.security.CustomUserDetailsService' in your configuration.
...
Do you have any idea what is problem? Is it good solution to exclude security?
There are classes:
#RestController
#AllArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
#GetMapping
public List<EmployeeDTO> getEmployees() {
return employeeService.getAllEmployees();
}
}
#WebMvcTest(controllers = EmployeeController.class)
class EmployeeControllerTest {
#Autowired
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Test
void getEmployees_Success() throws Exception {
// mockMvc.perform(get("/employee/all")).andExpect(status().isOk());
}

The controller which you are using in test have a dependency EmployeeService employeeService;
So in test case add below.
#Mock
EmployeeService employeeService;

Related

Different annotations for unit tests in Spring app

I am writing some unit tests and I am confused due to the different annotations in unit tests:
One of them is like:
#ExtendWith(SpringExtension.class)
class EmployeeServiceImplTest {
#MockBean
private EmployeeRepository employeeRepository;
#Autowired
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
And another is using the following annotations:
#RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceImplTest {
#Mock
private EmployeeRepository employeeRepository;
#InjectMocks
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
So, I use Java and Spring Boot for language and framework. So, which annotations should I use?
#ExtendWith(SpringExtension.class) or #RunWith(MockitoJUnitRunner.class),
#MockBean or #Mock,
#Autowired or #InjectMocks
Any idea regarding to these annotations?

Problem autowiring UserDetailsService while running tests

I added spring security to my application and it's working great, but now existing tests are broken, when I run them I get:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field userDetailsService in xxx.xxx.someapp.spring.security.WebSecurityConfig required a bean of type 'xxx.xxx.sardinatank.someapp.security.services.UserDetailsServiceImpl' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
This is how I'm wiring the UserDetailsServiceImpl in my WebSecurityConfig
#Autowired
UserDetailsServiceImpl userDetailsService;
This is an example controller
#RestController
public class HelloWorldController {
#GetMapping
public ModelAndView helloWorld() {
final ModelAndView bootstrapFront = new ModelAndView("index.html");
return bootstrapFront;
}
}
And this is a test that fails
#WebMvcTest(controllers = HelloWorldController.class)
#ContextConfiguration
#WebAppConfiguration
class HelloWorldControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getBootstrappedDoc() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andReturn();
assertEquals("index.html", mvcResult.getModelAndView().getViewName());
}
}
I guess you might have declared it as a #Component or something that is not part of this list:
#Controller, #ControllerAdvice, #JsonComponent, Converter, Filter, WebMvcConfigurer
which means UserdetailServiceImpl will not be part of the #SpringWebMVC sliced test context, hence spring doesn't bootstrap it.
You have 2 options:
Either you decorate your UserServiceImpl with one of the aforementioned annotations: e.g
#JsonComponent
class UserDetailsServiceImpl implements userDetailsService { ... };
Or you inject a mock of UserServiceImpl in your test with #MockBean:
#WebMvcTest(controllers = HelloWorldController.class)
#ContextConfiguration
#WebAppConfiguration
class HelloWorldControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
UserDetailsServiceImpl userDetailsService;
private MockMvc mockMvc;
//...
}

WebMvcTest with real service implementation

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();
}
}

UnsatisfiedDependencyException in a #WebMvcTest test class

My unit tests fail after I added #Service to the controller.
The project is Spring-boot v 2.0.1.RELEASE. I've spent many hours trying to find the answer but no luck. The test worked before I added #Service annotation and my service class has a repository in it.
Stack trace:
2018-04-24 12:57:12.487 WARN 940 --- [ main]
o.s.w.c.s.GenericWebApplicationContext : Exception encountered
during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fleetController': Unsatisfied
dependency expressed through field 'service'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'uk.co.vw.lead.service.ContactUsService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Controller:
#Slf4j
#RestController
#RequestMapping(value = VERSION, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public class FleetController {
public static final String VERSION = "1.0";
#Autowired
private ContactUsService service;
#InitBinder
public void initBinder(final WebDataBinder webDataBinder) {
webDataBinder.registerCustomEditor(NatureOfEnquiryEnum.class, new NatureOfEnquiryEnumConverter());
webDataBinder.registerCustomEditor(FleetSizeEnum.class, new FleetSizeEnumConverter());
}
#PostMapping(value = "/fleet/contact-us")
public ResponseEntity contactUs(#Valid ContactUsDTO formDTO) {
service.createForm(formDTO);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
#PostMapping(value = "/fleet/request-demo")
public ResponseEntity requestDemo(#Valid RequestDemoDTO demoDTO) {
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
Service:
#Service
public class ContactUsServiceImpl implements ContactUsService {
#Autowired
private FleetRepository repository;
#Override
public void createForm(ContactUsDTO formDTO) {
ContactUsForm form = populateContactUsForm(formDTO);
repository.save(form);
}
}
Test class:
#RunWith(JUnitPlatform.class)
#WebMvcTest(FleetController.class)
#ExtendWith(SpringExtension.class)
public class FleetControllerTest {
private final String CONTACT_US_URL = "/fleet/contact-us";
#Autowired
private MockMvc mockMvc;
#MockBean
private FleetRepository repository;
#Autowired
private ContactUsService service;
#Test
public void contactUsSuccessTest() throws Exception {
this.mockMvc.perform( post("/" + VERSION + CONTACT_US_URL)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("firstname", "John")
.param("lastname", "Doe")
.param("company", "Skynet")
.param("emailAddress", "john.connor#sky.net")
.param("telephone", "020 8759 4294")
.param("natureOfEnquiry", "new")
.param("comments", "some comments")
.param("recaptchaResponse", "success"))
.andExpect(status().isNoContent());
}
#Test
public void contactUsMissingRequiredFieldsTest() throws Exception {
this.mockMvc.perform( post("/1.0/fleet/contact-us")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
.andExpect(status().isBadRequest());
}
}
Please help as I have no idea what is going on.
Test classed annotated with #WebMvcTest are tests that focus only on the Spring MVC components : controllers.
So the service field declared in your unit test could not be autowired:
#Autowired
private ContactUsService service;
So you should mock also this dependency :
#MockBean
private ContactUsService service;
Note also that as FleetController doesn't have any direct dependency on FleetRepository, mocking this bean is not required :
#MockBean
private FleetRepository repository;
It is even worse as it adds a mock in the context that could create side effect during your test.
You have to mock only the direct dependencies of the controller under test.
As alternative if you want to mock only some beans and not all which are not controllers, don't use #WebMvcTest and instead of use #SpringBootTest that will load the whole context.
Then declare in the test class, the class(es) you want to mock with #MockBean.

junit testing service with injected repository

i have a service i need to test, it has injected repository
#Service
public class MyService {
#Inject
private MyRepo myRepo;
public someFunctionToTest(){
// body...
}
}
however i can not figure out how to write a proper junit tests for service
i have tryed following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Application.class })
#Configuration
public class NumberGeneratorServiceTest {
#Inject
private MyService myService;
#Inject
private MyRepository MyRepository;
#Test
public void test() {
//do something
}
}
in this case im getting error: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'getMyRepository': Requested bean is currently in creation: Is there an unresolvable circular reference?
what is the proper way to test Services with injections?
EDIT: MyRepository
#Repository
#AllArgsConstructor
#NoArgsConstructor
public class MyRepositoryImpl implements MyRepository {
#Inject
private MyJPARepository entityRepository;
//some methods
}

Categories

Resources