Java Class
public class SendNotification {
public NotificationProperty config;
public PostDbClient postDbClient;
public ObjectMapper objectMapper;
public AmazonSNS snsClient;
public NotificationParser notificationParser;
}
Test Class
#ExtendWith(MockitoExtension.class)
public class SendNotificationTest {
#Mock
AmazonSNS amazonSNS;
#Mock
public NotificationProperty config;
#Mock
public PostDbClient postDbClient;
#Mock
public ObjectMapper objectMapper;
#Mock
public NotificationParser notificationParser;
#InjectMocks
SendNotification sendNotification;
#BeforeEach
public void createMocks() {
// MockitoAnnotations.openMocks(this);
}
#Test
public void shouldSendNotification() {
System.out.println("Step1");
System.out.println(config);
System.out.println("Step2");
System.out.println(postDbClient);
System.out.println("Step3");
System.out.println(objectMapper);
System.out.println("Step4");
System.out.println(notificationParser);
System.out.println("Step5");
System.out.println(amazonSNS);
System.out.println("Step6");
Mockito.when(amazonSNS.publish(any())).thenReturn(new PublishResult());
// doReturn(new PublishResult()).when(amazonSNS).publish(new PublishRequest());
}
Exception
java.lang.NullPointerException
at java.base/java.lang.reflect.Method.getParameterTypes(Method.java:311)
at org.mockito.internal.creation.DelegatingMethod.<init>(DelegatingMethod.java:20)
at org.mockito.internal.invocation.DefaultInvocationFactory.createMockitoMethod(DefaultInvocationFactory.java:81)
at org.mockito.internal.invocation.DefaultInvocationFactory.createInvocation(DefaultInvocationFactory.java:60)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:83)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptSuperCallable(MockMethodInterceptor.java:145)
at com.expedia.ers.singleview.glasscase.client.GlassCaseClientFactory$MockitoMock$KUPO0Qd7.toString(Unknown Source)
at java.base/java.lang.String.valueOf(String.java:2951)
at java.base/java.io.PrintStream.print(PrintStream.java:759)
at org.gradle.internal.io.LinePerThreadBufferingOutputStream.println(LinePerThreadBufferingOutputStream.java:230)
Output
Step1
config
Step2
postDbClient
Step3
objectMapper
Step4
notificationParser
Step5
Dependencies
springBootVersion = '2.7.7'
testImplementation group: 'io.springfox', name: 'springfox-staticdocs', version: '2.6.1'
testImplementation 'org.junit.jupiter:JUnit-jupiter:5.6.2'
testImplementation 'org.mockito:mockito-inline:5.0.0'
test {
useJUnitPlatform()
dependsOn cleanTest
testLogging.showStandardStreams = true
maxParallelForks = 1
}
I am not able to mock any public interface in my whole spring boot project. Test Case in passing in intelij, failing while running gradle clean buid
Related
I am having dificulties with using MockMvc.
Here I have simple Service and controller classes:
Service:
#Slf4j
#Service
public class EmployeeService {
//...
public Employee GetSample() {
//...
//filling Employee Entities
return new Employee(
"Harriet"
, "random"
, 25);
}
}
controller:
#RestController
#RequestMapping(value = "/info")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#Validated
public class EmployeeController {
private final EmployeeService employeeService;
#PostMapping("/GetEmployee")
public ResponseEntity<Employee> GetEmployee() {
Employee employee = employeeService.GetSample();
return new ResponseEntity<>(employee, HttpStatus.OK);
}
}
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class EmployeeTestCase {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController EmployeeController;
#Mock
private EmployeeService employeeService;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(employeeController).build();
}
#Test
public void getEmployee() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.post("/info/GetEmployee")).andDo(print());
}
}
when I try to use MockMvc I get null body. It seems employee is null. But I didn't understand why.
I thought that when test uses perform, it should initialise employee and later on it should't be null.
I tried to cut the code as much as possible. I hope it is not long.
Note : also tried to use Mockito.when(employeeController.GetEmployee()).thenCallRealMethod();
The #SpringBootTest annotation loads the complete Spring application
context. That means you do not mock your layers
(Services/Controllers).
If you wanted to test specific layers of your application, you could look into test slice annotations offered by Springboot: https://docs.spring.io/spring-boot/docs/current/reference/html/test-auto-configuration.html
In contrast, a test slice annotation only loads beans required to test a particular layer. And because of this, we can avoid unnecessary mocking and side effects.
An example of a Test Slice is #WebMvcTest
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = HelloController.class,
excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class)
}
)
public class HelloControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void hello() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
#Test
public void helloDto() throws Exception {
String name = "hello";
int amount = 1000;
mvc.perform(
get("/hello/dto")
.param("name", name)
.param("amount", String.valueOf(amount)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.amount", is(amount)));
}
}
However if you really wanted to load up the SpringBoot Application context, say for an Integration Test, then you have a few options:
#SpringBootTest
#AutoConfigureMockMvc
public class TestingWebApplicationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class AuctionControllerIntTest {
#Autowired
AuctionController controller;
#Autowired
ObjectMapper mapper;
MockMvc mockMvc;
#Before
public void setUp() throws Exception {
System.out.println("setup()...");
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void create_ValidAuction_ShouldAddNewAuction() throws Exception {
final Auction auction = new Auction(
"Standing Desk",
"Stand up desk to help you stretch your legs during the day.",
"Johnnie34",
350.00);
mockMvc.perform(post("/auctions")
.contentType(MediaType.APPLICATION_JSON)
.content(toJson(auction)))
.andExpect(status().isCreated());
}
}
Lets say you don't want to load up any layers at all, and you want to mock everything, such as for example a Unit Test:
#RunWith(MockitoJUnitRunner.class)
class DemoApplicationTest {
#Mock
private UserRepository userRepository;
private Demo noneAutoWiredDemoInstance;
#Test
public void testConstructorCreation() {
MockitoAnnotations.initMocks(this);
Mockito.when(userRepository.count()).thenReturn(0L);
noneAutoWiredDemoInstance = new Demo(userRepository);
Assertions.assertEquals("Count: 0", noneAutoWiredDemoInstance.toString());
}
}
I use spring boot 2.3.2 with mapstruct.
In a service class I have a mapper who have an autowired annotation.
#Service
public BillingService{
private BillingRepository billingRepository;
#Autowired
private BillingMapper billingMapper;
#Autowired
public BillingService (BillingRepository billingRepository){
this.billingRepository=billingRepository;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
#RunWith(MockitoJUnitRunner.class)
public class BillingServiceTest{
#Mock
BillingRepository billingRepository;
private BillingService bilingService;
#Spy
private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#BeforeEach
public void setup(){
MockitoAnnotations.initMocks(this);
billingService = new BillingService();
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
When I debug and I'm stuck in getBilling method in BillingService Class, billingMapper is alway null;
You are using very strange configuration. First of all you have method returning BillingService that doesn't specify any return value so this would not even compile. I suggest following way:
#Service
public BillingService{
private final BillingRepository billingRepository;
private final BillingMapper billingMapper;
// constructor with bean injection
public BillingService(final BillingRepository billingRepository,
final BillingMapper billingMapper) {
this.billingRepository = billingRepository;
this.billingMapper = billingMapper;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
Then you can configure your test like following:
#RunWith(SpringJUnit4ClassRunner.class)
public class BillingServiceTest {
#Spy private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#MockBean private BillingRepository billingRepository;
#Autowired private BillingService billingService;
#TestConfiguration
static class BillingServiceTestContextConfiguration {
#Bean public BillingService billingService() {
return new BillingService(billingRepository, billingMapper);
}
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingRepository.findById(1L)).thenReturn(); // someResult
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
Similiar configuration should work. Main problem is that you are mixing #Autowired with setter/constructor injection (don't know since your weird method inside BillingService. Also dont know why you use #Spy annotation when you are tryning to Mock interface. #Spy is used to mock actual instance, while #Mock is mocking Class type. I would stick with #MockBean private BillingMapper billingMapper instead if BillingMapper is specified as Bean in your application.
I'm trying to #MockBean a #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {}
I'm injecting it into a #Service annotated class:
#Service
public class AuthorizationService {
private ApplicationDao appsDao;
private List<Application> allowedApplications;
#Autowired
public AuthorizationService(ApplicationDao appsDao) {
this.appsDao = appsDao; //<<MOCKED INJECTED BEAN>>
this.fillApplications();
}
private void fillApplications() {
this.appsDao.findAll() //<<MOCKED method>>
.forEach(entry -> {
this.allowedApplications.put(entry.getName(), entry);
});
}
public bool isAuthorized(Application application) {
return this.allowedApplications
.stream()
.anyMatch(app -> app.getId().equals(application.getId()));
}
}
My test mocking configuration looks like:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class GroupReferencesTest {
private #Autowired AuthorizationService;
private #MockBean ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app)); //<<FAILS>>
}
}
Nevertheless, my mocked object is not injected. I mean, when my AuthorizationService calls its injected ApplicationDao is returns an empty list instead of my mocked list.
I've tried to use #MockBean(name="applicationDao") as well. The behavior is the same.
I've also tried to configure my mocked bean using this code:
#TestConfiguration
public class RestTemplateTestConfiguration {
#Bean("applicationDao")
#Primary
public static ApplicationDao mockApplicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(mock.findAll())
.thenReturn(allowedApplications);
return mock;
}
}
However, it doesn't works right.
Application class is:
public class Application {
private String id;
//setters & getters
}
Any ideas?
First things first - the type of test. Answer: Unit test.
You are starting Spring context that manages a lifecycle of AuthorizationService and then you are trying to inject mock. What really happens is that Spring IoC container is injecting a real ApplicationDao (the one managed by Spring IoC container) into the AuthorizationService.
Solution:
Manage lifecyle of AuthorizationService by your test runner (like MockitoJUnitRunner and inject ApplicationDao mock into it):
#RunWith(MockitoJUnitRunner.class)
public class GroupReferencesTest {
private #InjectMocks AuthorizationService authorizationService;
private #Mock ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app));
}
}
Working example
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {AuthorizationService.class})
public class GroupReferencesTest {
#Autowired
private AuthorizationService;
#MockBean
private ApplicationDao applicationDao;
#Test
public void test() {
//given
Mockito.when(applicationDao.findAll()).thenReturn(emptyList());
//when & then
assertTrue(authorizationService.isAuthorized(app));
}
}
I have :
an interface : EntityService
a first implementation : EntityServiceImpl - This class is annotated with #Primary
an other one : EntityServiceClientImpl
and a controller that has this field #Autowired EntityService
I would like to do a test on this controller and for this test to be unitary I mock EntityService.
So of course this code does not work because Spring detects two beans annotated with Primary :
#Configuration
class EntityControllerTestConfig {
#Bean
#Primary
EntityService entityService() {
return mock(EntityService.class);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
#WebAppConfiguration
#ContextConfiguration(classes = EntityControllerTestConfig.class)
public class EntityControllerTest {
#Autowired
private EntityService entityService;
...
#SpringBootApplication(scanBasePackages= "com.company.app")
#EntityScan (basePackages = {"com.company.app" }, basePackageClasses = {Jsr310JpaConverters.class })
#EnableJpaRepositories("com.company.app")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
I tried to find an other way to mock and to exclude EntityServiceClient on test configuration but i was not able to mock. (cf : exclude #Component from #ComponentScan )
I finaly found that solution : a spring context (with controller, controllerAdvice and mock of service) and not a spring boot context
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class EntityControllerTest {
#Configuration
public static class EntityControllerTestConfig {
#Bean
public EntityService entityService() {
return mock(EntityService.class);
}
#Bean
public EntityController entityController() {
return new EntityController(entityService());
}
}
#Autowired
private EntityService entityService;
#Autowired
private EntityController entityController;
private MockMvc mockMvc;
#Before
public void setup() throws DomaineException {
this.mockMvc = MockMvcBuilders
.standaloneSetup(entityController)
.setControllerAdvice(new myControllerAdvice())
.build();
NB : Since Spring 4.2 you can set your ControllerAdvice like that.
You can approach it slightly differently and combine #WebMvcTest with #MockBean annotation to test just the controller with it's own minimal context.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = EntityController.class)
public class EntityControllerTest {
#MockBean
private EntityService entityService;
#Autowired
private MockMvc mvc;
In this example EntityService will be mocked while MockMvc can be used to assert the request mappings in controller.
I´m having issues with my test cases after having introduced #Autowired in one of the classes under test.
My testcase now looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext.xml", "/spring-security.xml"})
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
private StudentRepository studentRepository;
private CompanyRepository companyRepository;
private Student testStudent;
private Company testCompany;
#Before
public void setUp() {
studentRepository = new StudentRepository();
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
#Test
....
}
StudentRepository now looks like this:
#Service
public class StudentRepository extends AbstractRepository<Student> {
...
private PasswordEncoder passwordEncoder;
private MailService mailService;
public StudentRepository() {
// TODO Auto-generated constructor stub
}
#Autowired
public StudentRepository(MailService mailService, PasswordEncoder passwordEncoder) {
this.mailService = mailService;
this.passwordEncoder = passwordEncoder;
}
Obviously this test case won´t work anymore.
But what changes do I need to make to the testcase for the #Autowired annotation to be picked up by the test case?
EDIT:
I´ve now updated my setUp() to this (I need the password encoder to avoid null password):
#Before
public void setUp() {
//studentRepository = new StudentRepository();
studentRepository = new StudentRepository(mock(MailService.class), ctx.getAutowireCapableBeanFactory().createBean(ShaPasswordEncoder.class));
studentRepository.setJdbcTemplate(getJdbcTemplate());
testStudent = Utils.testStudentNoApplication();
}
My testcase is now running OK, but my testsuite failes with a NullPointerException.
I´m guessing the ApplicationContext is not being Autowired when running the testsuite for some reason?
If you don't want to declare your StudentRepository in one of XML files referenced by #ContextConfiguration and autowire it into the test, you can try to use AutowireCapableBeanFactory as follows:
...
public class StudentRepositoryTest extends AbstractDatabaseTestCase {
...
#Autowired ApplicationContext ctx;
#Before
public void setUp() {
studentRepository = ctx.getAutowireCapableBeanFactory()
.createBean(StudentRepository.class);
...
}
...
}