Mocked bean in spring unit test not wired to AutoWired dependency - spring

I am trying to write unit test for a class which has an Autowired dependency.
public class User {
#Autowired
private ServiceContext serviceContext;
User() {}
public String getToken() {
return serviceContext.getToken();
}
My Unit Test class to test getToken() method
#RunWith(SpringJUnit4ClassRunner.class)
public class UserTest() {
#MockBean
private ServiceContext serviceContext;
#BeforeTest
private void setup() {
when(serviceContext.getToken()).thenReturn("Token");
}
#Test
public void test() {
User user = new User();
assertEquals(user.getToken(), "Token");
}
}
When I run this test, there is a NullPointerException in getToken() of User . It says the serviceContext variable is null.
Why cant the mock bean which I created in the test be autowired to the dependency in User class ?
I also tried this test code -
#RunWith(SpringJUnit4ClassRunner.class)
public class UserTest() {
#MockBean
private ServiceContext serviceContext;
#InjectMocks
User useer = new User();
#BeforeTest
private void setup() {
when(serviceContext.getToken()).thenReturn("Token");
}
#Test
public void test() {
assertEquals(user.getToken(), "Token");
}
}
This also gives a NullPointerException saying that the serviceContext dependency in User class is null.
How do I unit test my User class methods using a mocked ServiceContext bean and wiring it to the User objects ?
I am using annotation based spring configuration and dont want to start the spring container to test this.
For running my application, I am using this-
#Configuration
#EnableConfigurationProperties(ApiProperties.class)
public class ServiceConfiguration {
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.TARGET_CLASS)
ServiceContext serviceContext(ApiProperties properties, Parameter param) {
final ServiceContext serviceContext = new ServiceContext(properties, param);
return serviceContext;
}
Do I need to add this class in my #SpringBootTest ?

How does spring know which context should be created?
You've only defined that the test should run with spring, but spring has no clue from where to load the configurations.
You should use #SpringBootTest annotation if you want to rely on spring boot configuration resolution rules or at some #ContextConfiguration to specify manually the configurations to load.

Related

Spring Boot integration test fails with "No qualifying bean" for MockMvc

I'd like to create an integration test for my Spring Boot application checking that a controller returns the correct HTTP status when sending an email.
This is how my test looks like:
#SpringBootTest
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
#Profile("test")
public class EmailControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Rule
public SmtpServerRule smtpServerRule = new SmtpServerRule(2525);
private static final String RESOURCE_PATH = "/mail";
#Test
public void whenValidInput_thenReturns200() throws Exception {
final EmailNotification emailNotification = EmailNotification.builder()
.emailAddress("foo#bar.com")
.subject("TEST_SUBJECT")
.content("TEST_CONTENT")
.build();
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(emailNotification))
).andExpect(status().isOk());
}
}
However it fails with the following excetpion:
No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I looked at the Spring Boot tutorials on integration testing but cannot see what's wrong.
This is the controller under test:
#RestController
public class EmailController {
private static final Logger LOG = LoggerFactory.getLogger(EmailController.class.getName());
private final EmailService emailService;
#Autowired
public EmailController(EmailService emailService) {
this.emailService = emailService;
}
#PostMapping(value = "/mail", consumes = MediaType.APPLICATION_JSON_VALUE)
public void send(#Valid #RequestBody EmailNotification emailNotification) {
try {
emailService.sendEmail(emailNotification);
} catch (MailException | MessagingException e) {
LOG.error("Error sending email: (recipient address: {}): {}", emailNotification.getEmailAddress(), e.getMessage());
}
}
}
Remove the #Profile and add #ActiveProfiles("test") from your test class they are not the same.
#Profile conditional evaluates the bean if the profile passed as argument is enabled, #ActiveProfiles change the profile to the profile passed as a argument, or in other words, activate it.

How to set and inject property Value from Test in Junit

My Service class uses a property set it application.properties
#Service
public class Service {
#Value("${my.prop}")
private String prop;
public Response doWork(String param1,String param2){
System.out.println(prop); //NULL!!!
}
}
I want to test it and set my own value:
Test Class:
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource(locations = "application.properties",properties = { "my.prop=boo" })
public class ServiceUnitTest {
#InjectMocks
private Service service;
#Test
public void fooTest(){
Response re = service.doWork("boo", "foo");
}
}
But when I run the test, the value is null (not even the value that exists in application.properties).
I don't have experience with MockitoJUnitRunner, but I am able to achieve this using PowerMockRunner.
Set up the application context with a test configuration, and autowire the bean from you application context into your test class. You also shouldn't need the "locations = " bit in the #TestPropertySource annotation.
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#TestPropertySource(properties = { "my.prop=boo" })
public class ServiceUnitTest {
#TestConfiguration
static class ServiceUnitTestConfig {
#Bean
public Service service() {
return new Service();
}
}
#Autowired
Service service;
#Test
public void fooTest(){
Response re = service.doWork("boo", "foo");
}
}

#Autowired Bean is NULL in Spring Boot JUnit Test

I'm trying to write the unit test case for the below file.
RoleDataController.Java
#RestController
#RequestMapping("/updateRoleData")
public class RoleDataController extends ControllerBase {
#Autowired
public EntityManager entityManager;
public Session session = entityManager.unwrap(Session.class);
RoleData _roleData = new RoleData();
#RequestMapping("/getRoleData")
public String findRoleData(){
List roleList =_roleData.findRoleData(session,123456);
return JsonHelper.toJson(roleList);
}
}
RoleDataControllerTest.java
public class RoleDataControllerTest {
RoleData _roleData = new RoleData();
#Autowired
public EntityManager entityManager;
public Session session = entityManager.unwrap(Session.class);
#Test
public void findRoleData() throws Exception {
List roleList =_roleData.findRoleData(session, 123456);
Assert.assertNotNull(roleList);
}
}
I'm getting NullPointerException in the below line
public Session session = entityManager.unwrap(Session.class);
Please help to fix this.
Unit tests don't start the Spring context and so the #Autowired annotation won't work - no dependency injection will be done by Spring. You either want to write an integration test or mock the behaviour of other beans.

How enable/disable #EnableGlobalMethodSecurity for #Service methods for testing scenario

I am working with Spring Framework and Spring Security
About Testing
For a set of Test classes for #Controller with security, .apply(springSecurity() and #WithUserDetails(value="something") are used
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity())// <---
.build();
}
For other set of Test classes for #Controller without security, therefore .apply(springSecurity()) and #WithUserDetails(value="something") are not used.
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.build();
}
Until here all about for #Controller with and without security work fine.
The problem is for the #Service, when #EnableGlobalMethodSecurity is defined and the #Service methods are annotated with #PreAuthorize("hasRole('ROLE_ADMIN')"), all the other Test classes for #Service where security is not required fail now with:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException:
An Authentication object was not found in the SecurityContext
Of course it because the #Test methods do not use #WithUserDetails(value="something")
Thus, practically .apply(springSecurity()) does the job, but it for a Web environment through MockMvcBuilders.webAppContextSetup(webApplicationContext)
But for the server side, where security is not needed, I have:
#Transactional
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContext.class})
#ActiveProfiles(resolver=TestActiveProfilesResolver.class)
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class PersonaServiceImplTest {
private static final Logger logger = LoggerFactory.getLogger(PersonaServiceImplTest.class.getSimpleName());
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private Environment environment;
...
Thus MockMvcBuilders.webAppContextSetup(webApplicationContext) has no sense to be used. What is the best way to resolve this?
You can use #WithUserDetails and #WithMockUser to test method security as well.
For the tests to pick up on method security, you need to include the class annotated with #EnableGlobalMethodSecurity in the component classes used for loading the ApplicationContext.
For example, if the configuration class SecurityConfig is annotated with EnableGlobalMethodSecurity
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig { }
And the Service MessageService has a method using #PreAuthorize.
#Service
public class MessageService {
public String getHelloMessage() {
return "Hello!";
}
#PreAuthorize("hasRole('ADMIN')")
public String getGoodbyeMessage() {
return "Goodbye!";
}
}
Then you need to include both of those classes in the MessageServiceTest and you can use the security testing annotations.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {SecurityConfig.class, MessageService.class})
public class MessageServiceTest {
#Autowired
MessageService messageService;
#Test
public void helloMessageReturnsHello() {
assertThat(messageService.getHelloMessage()).isEqualTo("Hello!");
}
#Test(expected = AuthenticationCredentialsNotFoundException.class)
public void goodbyeMessageWithoutUserThrowsException() {
messageService.getGoodbyeMessage();
}
#WithMockUser(roles = "ADMIN")
#Test
public void goodbyeMessageWithAdminReturnsGoodbye() {
assertThat(messageService.getGoodbyeMessage()).isEqualTo("Goodbye!");
}
}

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