Spring injection: #MockBean #Repository is not injected - spring

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

Related

Verifying pointcuts being called in tests

I have a dummy project where I try figure out how to test pointcuts being triggered.
My project consists of 1 aspect bean which just prints after a foo method is called
#Component
#Aspect
public class SystemArchitecture {
#After("execution(* foo(..))")
public void after() {
System.out.println("#After");
}
}
And a FooServiceImpl with implemented foo method
#Service
public class FooServiceImpl implements FooService{
#Override
public FooDto foo(String msg) {
return new FooDto(msg);
}
}
The code works and and I can see "#After" being printed to console, but I can't check programatically if after pointcut was called using the test below.
#SpringBootTest
public class AspectTest {
#Autowired
private FooService fooService;
#Test
void shouldPass() {
fooService.foo("hello");
}
}
I've also tried using non-bean proxy as was adviced in https://stackoverflow.com/a/56312984/18224588, but this time I'm getting an obvious error cannot extend concrete aspect because my spy proxy is no longer viewed as an aspect:
public class AspectNoContextTest {
#Test
void shouldPass() {
FooService fooService = Mockito.mock(FooService.class);
SystemArchitecture systemArchitecture = Mockito.spy(new SystemArchitecture());
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(fooService);
aspectJProxyFactory.addAspect(systemArchitecture);
DefaultAopProxyFactory proxyFactory = new DefaultAopProxyFactory();
AopProxy aopProxy = proxyFactory.createAopProxy(aspectJProxyFactory);
FooService proxy = (FooService) aopProxy.getProxy();
proxy.foo("foo");
verify(systemArchitecture, times(1)).after();
}
}
Ok, after some digging, I found that it's possible to accomplish this by making an aspect a #SpyBean. Also AopUtils can be used for performing additional checks
#SpringBootTest
public class AspectTest {
#Autowired
private FooService fooService;
#SpyBean
private SystemArchitecture systemArchitecture;
#Test
void shouldPass() {
assertTrue(AopUtils.isAopProxy(fooService));
assertTrue(AopUtils.isCglibProxy(fooService));
fooService.foo("foo");
verify(systemArchitecture, times(1)).after();
}
}

Spring Boot Unit Testing MockMvc Null Body

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

MapStruct mapper not initialized with autowired when debug

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.

Spring - How to test Controller with ApplicationEventPublisher dependency?

I have a Controller which is publishing an event
#RestController
public class Controller
{
#Autowired
private ApplicationEventPublisher publisher;
#GetMapping("/event")
public void get()
{
publisher.publishEvent(new Event());
}
}
Now I want to test that the event is published. First I tried to #MockBean the ApplicationEventPublisher and verify the method call. But this does not work according to https://jira.spring.io/browse/SPR-14335
So I am doing it like this:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = Controller.class)
public class ControllerTest
{
#Autowired
private MockMvc mockMvc;
#Test
public void getTest() throws Exception
{
this.mockMvc.perform(get("/").contentType(MediaType.APPLICATION_JSON)
.andExpect(status().isOk());
assertNotNull(Listener.event);
}
#TestConfiguration
static class Listener
{
public static Event event;
#EventListener
void listen ( Event incoming )
{
event = incoming;
}
}
}
Is there an easier way for this common use case?
You can do it like this
#RunWith(SpringRunner.class)
public class ControllerTest {
private MockMvc mockMvc;
#MockBean
private ApplicationEventPublisher publisher;
#Before
public void setup() {
Controller someController= new Controller(publisher);
mockMvc = MockMvcBuilders.standaloneSetup(someController).build();
}
#Test
public void getTest() throws Exception
{
ArgumentCaptor<Event> argumentCaptor = ArgumentCaptor.forClass(Event.class);
doAnswer(invocation -> {
Event value = argumentCaptor.getValue();
//assert if event is correct
return null;
}).when(publisher).publishEvent(argumentCaptor.capture());
this.mockMvc.perform(get("/").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
verify(publisher, times(1)).publishEvent(any(Event.class));
}
}
And also change Field Injection to Constructor Injection in your controller class(It is a good practice).
#RestController
public class Controller
{
private ApplicationEventPublisher publisher;
#Autowired
public Controller(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
....
}
im facing the same problem, and for now i solve the problem using TestConfiguration:
#SpringBootTest
class MyUseCaseIT {
#Autowired private ApplicationEventPublisher publisher;
#Autowired private MyService service;
#Test
void callUseCase() {
var event = mock(MyEvent.class);
doNothing().when(publisher).publishEvent(event);
service.useCase();
verify(publisher).publishEvent(event);
}
#TestConfiguration
static class MockitoPublisherConfiguration {
#Bean
#Primary
ApplicationEventPublisher publisher() {
return mock(ApplicationEventPublisher.class);
}
}
Another possiblity would be that you replace the ApplicationEventPublisher instance on your controller with the mock instance using reflection in your test:
public class ControllerTest {
...
// Collect your controller
#Autowired
private Controller controller;
// Use the mock publisher
#MockBean
private ApplicationEventPublisher publisherMock;
// E.g. in setup set the mock publisher on your controller
#Before
public void setup() {
ReflectionTestUtils.setField(controller, "publisher", publisherMock);
}
...

Spring JUnit testing with #Autowired annotation

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

Resources