I have a #SessionScope bean that keeps track of the current users role. When I run the application the value is present, however when I run my integration tests the bean is null.
Here's what I have:
#Component
#SessionScope
public UserSessionDataImpl implements UserSessionData {
private String role; // "Admin" or "User"
// getters/setters below
}
// Service
#Service("roleService")
public RoleServiceImpl implements RoleService {
#Autowired
UserSessionData sessionData;
public String getRole(){
return this.sessionData.getRole();
}
public String setRole(String role){
return this.sessionData.setRole(role);
}
}
// API
#Api
public class TicketApi {
#Autowired
private RoleService roleService;
#Autowired
private TicketService TicketService;
#RequestMapping(value = "person/{id}/tickets", method = RequestMethod.GET)
public String getTickets(long personId) {
// only admins can lookup tickets
if(roleService.getRoles.equals("Admin"){
// do logic
}
}
}
// Unit test method
#Before
public void setup(){
roleService.setRole("Admin"); //set role to admin for testing
}
#Test
// Calls TicketApi
public void getTicketsTest(){
mockMvc.perform(
get("/person/{id}/tickets")); // blows up due to null role
}
I am stumped as to why my roleSerivce loses the reference to sessionData. I do see that UserSessionDataImpl does get instantiated multiple times, which I wouldn't think would happen. I'm wondering if the mockMvc call creates a new Session which would cause the extra instantiations. Has anyone else figured this issue out?
Related
In tests i mock DateService to have the same date every time when i run the test, but when i use DateServie in other service then the mock retun null all the time. It is strange because the mock works in my custom date time provder. Here is the code:
Its work here:
#Service(MyDateTimeProvider.MY_DATE_TIME_PROVIDER)
public class MyDateTimeProvider implements DateTimeProvider {
public static final String MY_DATE_TIME_PROVIDER = "MyDateTimeProvider";
#Autowired
private DateService dateService;
#Override
public Optional<TemporalAccessor> getNow() {
return Optional.of(dateService.getCurrentDate().toInstant());
}
}
#Service
public class DateService {
public Date getCurrentDate() {
return new Date();
}
}
Its not work in the UserService:
#SpringBootTest
public class Test{
#MockBean
protected DateService dateService;
#BeforeEach
public void beforeEach() { Mockito.when(dateService.getCurrentDate()).thenReturn(DEFAULT_DATE_TIME.toDate());
}
...
}
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private DateService dateService;
private User createNewUser(final UserDto dto) {
User user = new User();
user.setEmail(dto.getEmail());
user.setRegistrationDate(dateService.getCurrentDate()); // i got null here
return userRepository.save(user);
}
}
What did i wrong? Thank you!
My colleague helped me. My problem was: i used "UserService" in a method with #PostConstuct annotation, so its run before the mock happened.
#RequiredArgsConstructor
#Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
#Transactional
#Override
public Long insertUser(UserSaveRequestDto userSaveRequestDto) {
Long user_id = userMapper.insertUser(userSaveRequestDto);
return user_id;
}
}
I'm trying to test the service layer.
I wonder which one should be further verified.
The code below simply verified whether the userMapper's insertUser was called or if the parameters were properly contained, what additional verification should be made?
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
UserMapper userMapper;
#InjectMocks
UserServiceImpl userService;
UserSaveRequestDto userSaveRequestDto;
UserResponseDto userResponseDto;
#BeforeEach
void setUp() {
userSaveRequestDto = UserSaveRequestDto.builder()
.userName("test")
.userPhoneNumber("01026137832")
.build();
userResponseDto = UserResponseDto.builder()
.userId(1L)
.userName("test")
.userPhoneNumber("01026137832")
.build();
}
// Mockito 이용한 테스트 코드
#DisplayName("Mock을 사용한 insertUser 테스트")
#Test
public void insertUser() {
// given
// when
Long userId = userService.insertUser(userSaveRequestDto);
// then
ArgumentCaptor<UserSaveRequestDto> captor = ArgumentCaptor.forClass(UserSaveRequestDto.class);
then(userMapper).should().insertUser(captor.capture());
assertThat(captor.getValue()).isEqualTo(userSaveRequestDto);
}
}
You should test if the returned userId has the expected value. Tell the userMapper mock to return a specific value and assert that the userService returned it as well.
assertThat(userId).isEqualTo(expectedIdValue);
I have simple Spring boot Rest application what returns Users list from database.
Application works as expected but test scenario fail with error. After long googling cannot figure out why?
It seems that test class cannot access userRepository and instead of calling userRepository.getAllUsers it calls AppController.getAllUsers.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
……………………………
Caused by: java.lang.NullPointerException
at com.loan.demo.controller.AppController.getAllUsers(AppController.java:43)
…………………………………………..
These are my classes:
LoanAppApplication
#SpringBootApplication
public class LoanAppApplication {
public static void main(String[] args) {
SpringApplication.run(LoanAppApplication.class, args);
}
}
Class User.java
#Entity
#Table(name="USERTABLE")
public class User {
private int id;
#NotNull
private String firstName;
#NotNull
private String lastName;
#NotNull
private String persID;
private int blocked;
private Set<Loan> loans;
public User() {
}
public User(String firstName, String lastName, String persID) {
this.firstName = firstName;
this.lastName = lastName;
this.persID = persID;
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<User, Integer>{
public User findById(int Id);
public User findByPersID(String userId);
}
And Rest Controller:
#RestController
public class AppController {
#Autowired
UserRepository userRepository;
#GetMapping("/doit")
public String doIt() {
return "Do It";
}
//list all users
#GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll(); // this is line 43 from debuging error log
}
}
And test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {LoanAppApplication.class})
public class LoanAppApplicationTests {
private MockMvc mockMvc;
#InjectMocks
private AppController appController;
#Before
public void addData() {
mockMvc = MockMvcBuilders.standaloneSetup(appController)
.build();
}
//First test scenario that return only string works perfectly
#Test
public void testData() throws Exception {
mockMvc.perform(get("/doit")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It"));
}
//but second that should return empty json string fails with exception
#Test
public void testGet() throws Exception {
mockMvc.perform(get("/users")
)
.andExpect(status().isOk())
.andExpect(content().string("Do It")); //this test should fail but not return exception
}
}
You need to mock your userRepository
#Mock
UserRepository userRepository;
so after #Mock you need to initialize Mock`s in #Before, add this:
MockitoAnnotations.initMocks(this);
then in code setup what users you want to get
User user = new User();
when(userRepository.getUsers()).thenReturn(Collections.singletonList(user));
and then check
verify(userRepository, times(1)).getUsers();
verifyNoMoreInteractions(userRepository);
this is because you application context is not working
Spring Boot 1.4 has a number of fine features including #DataJpaTest annotation that automatically wakes up the classpath embedded database for the test purposes. As far I know, it won't work in conjuction with TestRestTemplate in bounds of the same class.
The following test won't work:
#RunWith(SpringRunner.class)
#SpringBootTest
#DataJpaTest
public class PersonControllerTest {
private Logger log = Logger.getLogger(getClass());
private Category category;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private TestEntityManager entityManager;
#Before
public void init() {
log.info("Initializing...");
category = entityManager.persist(new Category("Staff"));
}
#Test
public void personAddTest() throws Exception {
log.info("PersonAdd test starting...");
PersonRequest request = new PersonRequest("Jimmy");
ResponseEntity<String> response = restTemplate.postForEntity("/Person/Add", request, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
log.info("PersonAdd test passed");
}
During startup of the test an exception will be thrown:
Unsatisfied dependency expressed through field 'restTemplate':
No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate]
Then guessing to switch to the recommended mock based slice approach but it won't work there because the controller looks like this:
#RequestMapping(value="/Person/Add", method=RequestMethod.POST)
public ResponseEntity personAdd(#Valid #RequestBody PersonRequest personRequest,
Errors errors)
personValidator.validate(personRequest, errors):
if (errors.hasErrors())
return new ResponseEntity(HttpStatus.BAD_REQUEST);
personService.add(personRequest);
return new ResponseEntity(HttpStatus.OK);
}
... it's easy to mock the personService as the documentation suggests but how to be with the errors object which is not mockable in this case? As far I know, there's no ways to mock it since it isn't class field or a returned value of a method.
So, I'm unable to test the code above using neither slice approach nor integration one since #DataJpaTest should not be used with a controller.
Is there a way to test the controller with such architecture using Spring Boot 1.4 testing features?
Your understanding of the #DataJpaTest is a little off. From the documentation "Can be used when a test focuses only on JPA components". If you are wanting to test your controller layer you don't want to use this annotation as none of the WebMvc components get loaded into the application context. You instead want to use the #WebMvcTest and have it use the #Controller that you are testing.
#RunWith(SpringRunner.class)
#WebMvcTest(PersonController.class)
public class PersonControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
PersonValidator personValidator;
#MockBean
PersonService personService;
#Test
public void personAddTest() throws Exception {
String content = "{\"name\": \"Jimmy\"}";
mockMvc.perform(post("/Person/Add").contentType(MediaType.APPLICATION_JSON).characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON).content(content)).andExpect(status().isOk());
}
#Test
public void personAddInvalidTest() throws Exception {
String content = "{\"noname\": \"Jimmy\"}";
mockMvc.perform(post("/Person/Add").contentType(MediaType.APPLICATION_JSON).characterEncoding("UTF-8")
.accept(MediaType.APPLICATION_JSON).content(content)).andExpect(status().isBadRequest());
}
}
Not sure how you wired the validator and service so just assumed you autowired them.
#Controller
public class PersonController {
private PersonValidator personValidator;
private PersonService personService;
public PersonController(PersonValidator personValidator, PersonService personService) {
this.personValidator = personValidator;
this.personService = personService;
}
#RequestMapping(value = "/Person/Add", method = RequestMethod.POST)
public ResponseEntity<String> personAdd(#Valid #RequestBody PersonRequest personRequest, Errors errors) {
personValidator.validate(personRequest, errors);
if (errors.hasErrors()) {
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
personService.add(personRequest);
return new ResponseEntity<String>(HttpStatus.OK);
}
}
Sample PersonRequest as I didn't know what else was in there. Note the one validation on the name as being #NotNull as I wanted a way to show how to use the Errors object.
public class PersonRequest {
#NotNull
private String name;
public PersonRequest() {
}
public PersonRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have below Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfig.class)
public class UserServiceTest {
#Inject
private UserRepository userRepository;
#Inject
private UserService userService;
#Test
public void testProcessInvoice() throws SQLException {
User user = new User();
user.setFirstName("abc");
when(userRepository.save(any(User.class))).thenReturn(user);
Assert.assertNotNull(userService);
User savedUser = userService.save(user);
Assert.assertEquals("abc", savedUser.getFirstName());
}
}
I have below SpringTestConfig.java
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
call to User savedUser = userService.save(user); returns null user object. I am not able to figure it out why it is returning null.
EDIT:
UserRepository is JpaRepository, if this is a problem
public interface UserRepository extends JpaRepository<User, Long> {
}
Your UserService is a mock object, and has no defined behavior for dealing with the #save(User) method.
Mocking the object under test is probably not what you are after here. I would recommend your objects under test are instantiated in the test, and injected with the mocks or stubs of the objects that they utilize.
Your configuration needs to return a real UserService:
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return new UserServiceImpl(); // or whatever your implementation is
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
Mocks are for collaborators, not for the thing you're testing.