Unit testing field validation with MockMvc but without Spring context? - validation

Is it possible to test #Valid annotations using MockMvc and MockitoJUnitRunner? I can test most of the behavior of my CRUD controller but validation seems to require the use of Spring's JUnit runner, building the entire context and creating the JPA repo implementation which requires a lot of stuff.
The test below tries to test a POST method receiving a Customer entity where the firstName field is annotated with #Size(min=2, max=20). The result is
java.lang.AssertionError: View name expected:<edit> but was:<redirect:/info>
So the validation did not run.
#RunWith(MockitoJUnitRunner.class)
public class DataControllerTest {
#Mock
CustomerRepository mockRepo;
#InjectMocks
private DataController controller;
MockMvc mockmvc;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockmvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testBadSubmit() throws Exception {
mockmvc.perform(MockMvcRequestBuilders.post("/edit/1")
.param("firstName", "a"))
.andExpect(MockMvcResultMatchers.view().name("edit"));
Mockito.verifyZeroInteractions(mockRepo);
}
}
Controller class:
#Controller
public class DataController {
#Autowired
public CustomerRepository crep;
...
#RequestMapping(value = {"/edit/{id}"}, method = RequestMethod.POST)
public String add(Model model, #Valid Customer customer, Errors result) {
if (result.hasErrors()) {
return "edit";
}
crep.save(customer);
return "redirect:/info";
}
Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id = null;
#Column(length=20)
#Size(min=2, max=20)
private String firstName;
...
}
JPA Repository interface:
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}

The purpose of the SpringJUnit4ClassRunner is to automatically load the application context and wire up everything automatically. You should be able to use MockitoJUnitRunner but you'll have to load the application context you want to use manually in your test. Regardless, however, you do need to load an application context, because calling DataController#add() through Spring is the only way that the #Valid annotation will be processed.
EDIT: If the real problem here is loading the JPA repository, you can use MockitoJUnitRunner and just load a test application context where a mock JPA repository is manually wired in, at little to no expense.

Related

NPE when not injecting mock data for mapper class (spring boot)

I am using spring boot. I have the below mapper class to map the entity object to model object.
#Mapper(componentModel = "spring")
public interface PersonMapper {
PersonEntity personModelToPersonEntity(PersonModel pm)
PersonModel personEntityToPersonModel(PersonEntity pe)
}
Service class:
#Service
public class MyService{
#Autowired
PersonMapper personMapper;
#Autowired
PersonRepository personRepo;
#Transactional
public List<Person> getDetails() {
List<Person> personList = personRepo.findAll();
List<PersonModel> pm = personMapper.personEntityToPersonModel(personList);
...
...
}
}
I have the below Junit test case, where I'm mocking the data.
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
// #Mock
// PersonMapper personMapper;
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
given(personRepo.findAll()).willReturn(mymockData);
//given(personMapper.personEntityToPersonModel(..).willReturn(..);
...
}
}
Eveything works if I mock the personMapper, but i don't want to mock the mapper class, when test case is executing, as i'm sending the mock entity data using "given(personRepo.findAll()).willReturn(mymockData);" so when it hits the service it should automatically convert the mock data sent to model object. In my case when i have commented the code for mocking mapper class, it is throwing NullPointerException in service class at the mapper object.
As #M. Denium said, nothing is there, because application context isn't raising with MockitoExtension. So, you can either mock your PersonMapper and stub its behavior or inject it manually with ReflectionTestUtils:
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
PersonMapper personMapper = Mappers.getMapper(PersonMapper.class);
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
ReflectionTestUtils.setField(personService, "personMapper", personMapper);
given(personRepo.findAll()).willReturn(mymockData);
...
}
}

Testing service layer in Spring Boot using Mockito

I am trying to unit test my service layer using mockito having a connection to a Database through the Repository layer.
My test case:
#InjectMocks
private EmployeeService employeeService;
#Mock
private EmployeeRepository employeeRepository;
#Test
public void getActionFromEmployeeIdTest() throws ActionException {
//setup
when(employeeRepository.getActionsByEmployeeId(anyLong()).thenReturn(EmployeeEntity);
// exercise
List<Employee> result = employeeService.getActionsByEmployeeIdService(101);
//verify
assertEqual(EmployeeEntity, result);
}
Service Layer:
#Service
public class EmployeeService {
#Override
public List<EmployeeUser> getActionsByEmployeeIdService(long employeeId) {
Employee employee = employeeRepository.findByEmployeeIdId(employeeId);
List<EmployeeUser> actions = employeeUserRepository.getActionsByEmployeeId(employeeId);
return actions;
}
Repository layer:
#Repository
public interface EmployeeUserRepository extends JpaRepository<EmployeeUser,Long> {
#Transactional
#Query(value = "Select e from EmployeeUser e where e.employeeId = :employeeId" )
List<EmployeeUser> getActionsByEmployeeId(#Param("employeeId") long employeeId);
}
I am using #InjectMocks for employeeService and #Mock for employeeRepository.
result seems to be returning null. Is there anyway I can return a non-null value in my test?
You are using reference of employeeRepository to call getActionssByEmployeeId(anyLong()) method.
when(employeeRepository.getActionssByEmployeeId(anyLong()).thenReturn(EmployeeEntity);
But in service calss this methos is getting called by employeeUserRepository
List<EmployeeUser> actions = employeeUserRepository.getActionsByEmployeeIdId(employeeId);
Please check this.

Spring Data Rest testing

I'm developing the application by using Spring Data Rest. As you know, after creating simple repository interfaces, rest-endpoints are created by library.
Do I need to test these endpoints by integration tests? If yes, please provide any examples
here is the code snippet. read the full tutorial here
#Entity
#Table(name = "person")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min = 3, max = 20)
private String name;
// standard getters and setters, constructors
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
public Employee findByName(String name);
}
#RunWith(SpringRunner.class)
#SpringBootTest(
SpringBootTest.WebEnvironment.MOCK,
classes = Application.class)
#AutoConfigureMockMvc
#TestPropertySource(
locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#Autowired
private EmployeeRepository repository;
#Test
public void givenEmployees_whenGetEmployees_thenStatus200()
throws Exception {
createTestEmployee("bob");
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].name", is("bob")));
}
}
Aside from regular testing you can do with Spring (using MockMvc, RestAssured, RestTemplate etc), Traverson is a great API for specifically testing Spring HATEOAS. You can use it to test the validity of the links you are returning to your clients, see this example

How to inject a Validator in the JUnit test of a SpringBoot-based service?

I'm building a SpringBoot CRUD application based on a REST controller calling a Spring Service. The POJO received have validation-related annotations (including custom validators) and the actual validations are triggered inside the Service (see below).
Everything works perfectly fine in a SpringBoot execution.
I now need to build relevant unit test cases for my Service i.e I do not want to start an application server via a SpringBootRunner.
Below is my Patient class with validation-related annotations.
#Data
#Entity
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotEmpty(message = "patient.name.mandatory")
private String name;
#Past(message = "patient.dateOfBirth.inThePast")
#NotNull(message = "patient.dateOfBirth.mandatory")
private Date dateOfBirth;
private boolean isEmergency;
// [...]
}
This is the Service called by SpringBoot's REST controller.
#Service
#Validated
public class PatientService {
#Autowired
private PatientRepository repository;
#Autowired
private Validator validator;
public Patient create(Patient patient) {
if (! patient.isEmergency()) {
validator.validate(patient);
// then throw exception if validation failed
}
// [...]
}
}
And here is my JUnit test.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#Import(ModalityTestConfiguration.class)
public class PatientServiceTest {
#InjectMocks
private PatientService service;
#MockBean
private PatientRepository repository;
#Autowired
private Validator validator;
#Test
public void invalidEmptyPatientNoEmergency() {
Patient p = new Patient();
Patient result = null;
try {
result = service.create(p); // validations must fail -> exception
assert(false);
} catch (ConstraintViolationException e) {
// Execution should get here to verify that validations are OK
assert(result != null);
assert(e.getConstraintViolations() != null);
assert(e.getConstraintViolations().size() != 0);
// [...]
} catch (Exception e) {
assert(false);
}
}
Just in case, here is the REST controller (not relevant I think as not related to the JUnit test)
#RestController
public class PatientController {
#Autowired
private PatientService patientService;
#PostMapping("/patients")
Patient createPatient(#RequestBody Patient patient) {
return (patientService.create(patient));
}
My problem is that the Validator in my Service is always null.
I have tried creating a dedicated configuration file with a validator bean
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
and linking the configuration to the Test case
#ContextConfiguration(locations = {"/modality-test-config.xml"})
I have tried to define a local #Bean for the Validator
I have played around with and without #Autowire
I have changed the #RunWith
I'm sure it must be a minor detail somewhere but I just don't seem to get a not-null validator inside the JUnit test of the Service.
UPDATE
Here is the TestConfiguration class I have added following TheHeadRush comment.
#TestConfiguration
public class ModalityTestConfiguration {
#Bean("validator")
public Validator validator() {
return (Validation.buildDefaultValidatorFactory().getValidator());
}
}
I have also added the corresponding #Import annotation in the Test class above.
Still no luck: the Validator field remains null in both the Test class and in the Service. Also, the breakpoint in the TestConfiguration class doesn't seem to get called.

How to mock Spring Data and unit test service

I'm trying to unit test a service method. The service methods calls a spring data repository method to fetch some data. I want to mock that repository call, and supply the data myself. How to do that? Following Spring Boot documentation, when I mock the repository and call the repository method directly in my test code, the mock is working. But when I call the service method, which in turn would call the repository method, mocking isn't working. Below is the sample code:
Service class:
#Service
public class PersonService {
private final PersonRepository personRepository;
#Autowired
public PersonService(personRepository personRepository) {
this.personRepository = personRepository;
}
public List<Person> findByName(String name) {
return personRepository.findByName(name); // I'd like to mock this call
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
For Spring Data repositories you need to specifiy the bean name. Mocking via type doesn't seem to work because the repository is a dynamic proxy at runtime.
The default bean name for PersonRepository is "personRepository", so this should work:
#MockBean("personRepository")
private PersonRepository personRepository;
Here's the complete test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
// http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans
#MockBean("personRepository")
private PersonRepository personRepository;
#Autowired
private PersonService personService;
private List<Person> people = new ArrayList<>();
#Test
public void contextLoads() throws Exception {
people.add(new Person());
people.add(new Person());
given(this.personRepository.findByName("Sanjay Patel")).willReturn(people);
assertTrue(personService.findByName("Sanjay Patel") == 2); // fails
}
}
Probably the repository is marked with #MockedBean annotation. I do not know if Spring can auto wire by type if the repository is a mock.
You can define the #Bean method and return Mockito.mock(X.class), this should work.
Not sure you need spring for unit testing a service method though. A lighter approach would be to use solely Mockito with its #InjectMocks annotation.

Resources