Spring boot 2.1 test fails because of null property - java

I'm not an expert in Spring Boot. I have to write tests for my #RestController methods but I have a problem, which is, the #AutoWired ConfigurationProperties class is null when the test class executes the main controller. I found many posts about a similar issue here but they really don't solve this problem. The strange thing is that in the #PostConstruct method of the #RestController the property class is not null, it is null only in the #RequestMapping methods I'm trying to test.
This is my #SpringBootApplication class:
#SpringBootApplication
#ComponentScan
#EnableConfigurationProperties({MysqlProperties.class, CassandraProperties.class, GenericsProperties.class})
#EnableAutoConfiguration
public class REST {
public static void main(String[] args) {
SpringApplication.run(REST.class, args);
}
}
This is the #RestController:
#RestController
public class MainController {
#Autowired
private MysqlProperties mysqlProperties;
#PostConstruct
public void init() throws Exception {
//Here mysqlProperties is not null and I can get elements from it
}
#RequestMapping(value = "/online", method = RequestMethod.GET)
public #ResponseBody
String online(#RequestHeader(value = "email", required = true) String email, #RequestHeader(value = "password", required = true) String password) {
Utils.logInfo(logger, "/online endpoint");
//Here mysqlProperties is null
String sql = "SELECT * FROM " + mysqlProperties.getAddress() + " WHERE email= ?";
return new Return(Return.ERROR_MESSAGE, "Access denied, not superuser").toString();
}
This is the #ConfigurationProperties class:
#Configuration
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "mysql")
public class MysqlProperties {
String address;
String database;
...
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
}
This is the test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {REST.class})
#EnableConfigurationProperties({CassandraProperties.class, GenericsProperties.class, MysqlProperties.class})
#AutoConfigureMockMvc
public class MainControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private GenericsProperties genericsProperties;
#Before
public void init() {
try {
//mc.init();
mvc = MockMvcBuilders.standaloneSetup(new MainController()).build();
} catch (Exception ex) {
ex.printStackTrace();
Logger.getLogger(MainControllerTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Test
public void testOnline() throws Exception {
//Return returnObject = new Return(Return.DONE_MESSAGE, "System online");
Return returnObject = new Return(Return.ERROR_MESSAGE, "Access denied, not superuser");
this.mvc.perform(get("/online")
.header("email", genericsProperties.getSuperuser_email())
.header("password", genericsProperties.getSuperuser_password()))
//.contentType(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(returnObject.toString()));
}
}
This is the package structure:
main
--java
----configurations
------MysqlProperties.java
----main
------MainController.java
----...
--resources
----application.properties
test
--java
----main
------MainControllerTest.java
The NullPointerException occurs in the MainController class at:
mysqlProperties.getAddress()
Any clue why it is not working? Thank you.

#Autowired MysqlProperties mysqlProperties is null bacause you created a new instance of MainController here:
mvc = MockMvcBuilders.standaloneSetup(new MainController()).build();
That instance will not be registered in the Spring context, therefore it will not be available for dependency injection.
You could read more about this issue here
You should use an autowired MainController in your MainControllerTest class.
#Autowired
private MainController mainController;

Related

Cannot find why jUnit test return NullPointerException

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

MockMvc and losing #SessionScope bean value

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?

Changing Spring XML configuration to #Configuration

My spring application is already configured via xml stye.I am trying to change it with #Configuration classes.
My app is used another project as maven library.I have a service, annotated with #Named and this service is used by another service in the library.
#Named("userDetailsService")
public class UserDetailsServiceImpl extends AbstractServiceImpl implements UserDetailsService {
#Inject
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.getByUserName(username);
}
}
#Named("userService")
public class UserServiceImpl extends BaseDaoServiceImpl<User, UserDao> implements UserService {
#Inject
private AuthorityService authorityService;
#Inject
private UserAuthorityService userAuthorityService;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User getByUserName(String username) {
return dao.getByUserName(username);
}
#Override
public List<User> getUserWithHasAuthority(String authorityName) {
return dao.getUserWithHasAuthority(authorityName);
}
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User insert(User user) {
user.setEnabled(true);
super.insert(user);
Authority authority = authorityService.getByName("ROLE_USER");
UserAuthority userAuthority = new UserAuthority();
userAuthority.setAuthority(authority);
userAuthority.setUser(user);
userAuthorityService.insert(userAuthority);
return user;
}
}
On my new #Configuration class
#Configuration
#ComponentScan(basePackages = {"com.mylibrary","com.myapp"})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableTransactionManagement
public class ServiceTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and this is my test method:
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
it gives null pointer exception on userService property that exist in userDetailsService.Both of them #Named.
Thnx for your help
Test classes :
#ContextConfiguration(classes = {ServiceTestConfiguration.class, DataSourceConfiguration.class, SecurityConfiguration.class})
#Transactional
public class AbstractTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
public class ServiceTest extends AbstractTest {
#Inject
private AuthenticationManager authenticationManager;
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
}

Spring Boot 1.4 - how to test a controller with the validation

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

bean validation spring + hibernate annotion does not work

What am I doing wrong? The test does not work.
This is my Interface class:
#Validated
public interface ICustomerService
{
public List<Customer> findbyStr(
#NotEmpty(message = "{column.notvalid}")
#NotNull(message = "{column.notvalid}")
String column,
#NotEmpty(message = "{column.notvalid}")
#NotNull(message = "{value.notvalid}")
String value);
}
This is my Implementation class:
#Service("customerService")
#Scope(value = "singleton", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CustomerService implements ICustomerService {
#Autowired
private IStorageDao<Customer> dao;
#Override
public List<Customer> findbyStr(String column, String value) {
return dao.findByString(Customer.class, column, value);
}
}
This is my unit-Test class:
JUNIT Test does not work.
#RunWith(SpringJUnit4ClassRunner.class)
public class CustomerTest extends BaseIntegrationTest {
#Autowired
private ICustomerService service;
#Autowired
public static Validator validator;
#Test
public void test_A6_CustomerFindByStrNull() {
List<Customer> result = service.findbyStr(null, null);
Set<ConstraintViolation<ICustomerService>> constraintViolations = validator
.validate(service);
assertEquals(0, constraintViolations.size());
assertEquals("Die angegebene E-Mail-Adresse ist fehlerhaft.",
constraintViolations.iterator().next().getMessage());
assertNotNull(result);
assertNotNull(result.get(1));
}
}
I'm pretty sure you cannot test ConstraintViolations when the annotations are on a method of an object since it should throw a MethodConstraintViolationException. You should try something like this :
#RunWith(SpringJUnit4ClassRunner.class)
public class CustomerTest extends BaseIntegrationTest {
#Autowired
private ICustomerService service;
#Test
public void test_A6_CustomerFindByStrNull() {
try {
List<Customer> result = service.findbyStr(null, null);
} catch (MethodConstraintViolationException ex) {
assertEquals("Die angegebene E-Mail-Adresse ist fehlerhaft.", ex.getConstraintViolations().iterator().next().getMessage());
}
fail("Exception expected");
}
}
You need to have the following Bean in your application-context.xml file :
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

Categories