I want to use a mock DAO to unit test the service layer in my spring application. In the DAO, it's seesinoFactory is injected using #Inject.
When the test class is configured with #RunWith(MockitoJUnitRunner.class)
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
#Mock
private MyDao myDaoMock;
#InjectMocks
private MyServiceImpl myService;
}
The output is just as expected.
When I changed the configuration to using #RunWith(SpringJUnit4ClassRunner.class)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/ServiceTest-context.xml")
public class ServiceTest {
#Mock
private MyDao myDaoMock;
#InjectMocks
private MyServiceImpl myService;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
}
}
The exception,"No qualifying bean of type [org.hibernate.SessionFactory] found for dependency", will be thrown if sessionFactory bean is not available in ServiceTest-context.xml.
What I don't understand is MyDao is annotated with #Mock already, why is sessionFactory still needed?
you must use #RunWith(MockitoJUnitRunner.class) to initialize these mocks and inject them.
#Mock creates a mock.
#InjectMocks creates an instance of the class and injects the mocks that are created with the #Mock or #Spy annotations into this instance.
Or Using Mockito.initMocks(this) as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("test-app-ctx.xml")
public class FooTest {
#Autowired
#InjectMocks
TestTarget sut;
#Mock
Foo mockFoo;
#Before
/* Initialized mocks */
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void someTest() {
// ....
}
}
Related
I'm writing unit tests for a Spring project with Junit 5 and Mockito 4.
I have to test a class that takes 2 objects via constructor and other 2 via #Autowired. I need to mock those 4 objects, so I annotated them with #Mock in my test class and then annotated the tested class with #InjectMocks.
I thought that #InjectMocks would inject my 4 mocks into myService, but it's only injecting the 2 that are passed by constructor, while the other 2 are null.
I'm looking for a solution that doesn't implies changes in the tested service.
The tested class looks like this:
#Service
public class MyService {
private String key = "KEY";
#Autowired
private FirstApiWrapper firstApiWrapper;
#Autowired
private SecondApiWrapper secondApiWrapper;
private MyRepository myRepository;
private OtherService otherService;
#Autowired
public MyService(
MyRepository myRepository,
OtherService otherService
) {
super();
this.myRepository = myRepository;
this.otherService = otherService;
}
My test class looks like this:
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
MyRepository myRepository;
#Mock
OtherService otherService;
#Mock
FirstApiWrapper firstApiWrapper;
#Mock
SecondApiWrapper secondApiWrapper;
#InjectMocks
MyService myService;
Any ideas of what is wrong with my code?
Thank you all very much!
-- I've also tried something based on this question:
#Mock
FirstApiWrapper firstApiWrapper;
#Mock
SecondApiWrapper secondApiWrapper;
#InjectMocks
MyService myService;
#BeforeEach
private void setUp() {
myService = new MyService(
Mockito.mock(MyRepository.class),
Mockito.mock(OtherService.class)
);
}
But the result is exactly the same. Also, if I delete repository and service instances and try to inject only the wrappers, It still fails!
I found a way to solve it without rewriting the existing code, by adding this to the test class:
#BeforeEach
private void setUp() {
MockitoAnnotations.openMocks(this);
}
But I'm not sure if it is a "correct" way of doing it.
Once of the issues with fields autowiring is that mockito won't be able to inject anything. So why do you need to mix the styles of injection if you already have a constructor-injection?
Rewrite your class:
#Service
public class MyService {
private String key = "KEY";
private final MyRepository myRepository;
private final OtherService otherService;
private final FirstApiWrapper firstApiWrapper;
private final SecondApiWrapper secondApiWrapper;
#Autowired // if its the only constructor in the class, you can omit #Autowired, spring will be able to call it anyway. You can even use Lombok to generate this constructor for, so you won't need to even write this method
public MyService(
MyRepository myRepository,
OtherService otherService,
FirstApiWrapper firstApiWrapper,
SecondApiWrapper secondApiWrapper
) {
this.myRepository = myRepository;
this.otherService = otherService;
this.firstApiWrapper = firstApiWrapper;
this.secondApiWrapper = secondApiWrapper;
}
With this design you can safely use #Mock / #InjectMocks annotations in the test
Mockito will create the instance of the class and inject relevant mocks.
I created Dao Repository that uses jdbc for working with DB.
I autowired this repository in my Service class.
Then I try to autowire my service class in my test class.
#SpringBootTest
public class ServiceTest {
#MockBean
private Dao dao;
#Autowired
private Service service;
#Test
void whenSomething_thanSomething() {
when(Dao.getStatus(anyString())).thenReturn("green");
assertEquals(0, service.getStatus(""));
}
//other tests...
}
#Service
public class Service {
private DaoImpl daoImpl;
#Autowired
public Service(DaoImpl daoImpl) {
this.daoImpl = daoImpl;
}
//...
}
#Repository
public class DaoImpl omplements Dao {
private NamedParameterJdbcOperations jdbc;
#Autowired
public DaoImpl(NamedParametedJdbcOperations jdbc) {
this.jdbc = jdbc;
}
//...
}
When I start test I get the next error:
Parameter 0 of constructor in Service required a bean of type DaoImpl that could not be found.
How can I resolve my problem?
Since you inject DaoImpl in your service-class you were probably intending to mock DaoImpl instead of Dao:
#SpringBootTest
public class ServiceTest {
#MockBean
private DaoImpl daoImpl;
...
}
I am using spring boot. I have the below mapper class to map the entity object to model object.
#Mapper(componentModel = "spring")
public interface PersonMapper {
PersonEntity personModelToPersonEntity(PersonModel pm)
PersonModel personEntityToPersonModel(PersonEntity pe)
}
Service class:
#Service
public class MyService{
#Autowired
PersonMapper personMapper;
#Autowired
PersonRepository personRepo;
#Transactional
public List<Person> getDetails() {
List<Person> personList = personRepo.findAll();
List<PersonModel> pm = personMapper.personEntityToPersonModel(personList);
...
...
}
}
I have the below Junit test case, where I'm mocking the data.
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
// #Mock
// PersonMapper personMapper;
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
given(personRepo.findAll()).willReturn(mymockData);
//given(personMapper.personEntityToPersonModel(..).willReturn(..);
...
}
}
Eveything works if I mock the personMapper, but i don't want to mock the mapper class, when test case is executing, as i'm sending the mock entity data using "given(personRepo.findAll()).willReturn(mymockData);" so when it hits the service it should automatically convert the mock data sent to model object. In my case when i have commented the code for mocking mapper class, it is throwing NullPointerException in service class at the mapper object.
As #M. Denium said, nothing is there, because application context isn't raising with MockitoExtension. So, you can either mock your PersonMapper and stub its behavior or inject it manually with ReflectionTestUtils:
#ExtendWith(MockitoExtension.class)
public class PersonTest{
#InjectMocks
PersonService personService;
PersonMapper personMapper = Mappers.getMapper(PersonMapper.class);
#Mock
PersonRepository personRepo;
#Test
void getPersonDetailsTest() {
ReflectionTestUtils.setField(personService, "personMapper", personMapper);
given(personRepo.findAll()).willReturn(mymockData);
...
}
}
My unit test try #Spy beanA. But BeanA #autowire bean B as bellow:
#RunWith(SpringRunner.class)
public class MyServiceImplTest {
#Spy
private BeanA beanA;
#InjectMocks
private MyService myService = new MyServiceImpl();
#Test
public void testDoSomeThing(){
myService.doSomeThing();
}
}
MyServiceImpl as bellow :
#Service
public class MyServiceImpl {
#Autowired
private BeanA beanA;
public doSomeThing(){
....
beanA.beanADoSomeThing()
....
}
}
beanA as bellow
#Service
public class BeanA {
#Autowired
private BeanB beanB;
public beanADoSomeThing(){
...
//Null pointer exception in here because beanB=null
beanB.beanBDoSomeThing()
}
}
when run unit test i get null pointer exception at line beanB.beanBDoSomeThing(), I can understand the reason but how to resolve this issue?
I have tried
#Mock
private BeanB beanB;
But this not work, how to resolve this issue ?
If you want to spy your bean in context, you need #SpyBean annotation instead of #Spy and also you should autowire your service to be tested, smth like this:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ExTest {
#SpyBean
private BeanA beanA;
#Autowired
private MyService myService;
#Test
public void testDoSomeThing() {
myService.doSomeThing();
}
}
If you don't want to load application context and test only MyServiceImpl behavior in isolation, you can use pure Mockito and mock or spy dependencies of MyServiceImpl:
#RunWith(MockitoJUnitRunner.class)
public class MockitTest {
#InjectMocks
private MyServiceImpl myService;
#Mock
private BeanA beanA;
#Test(expected = RuntimeException.class)
public void test() {
doThrow(new RuntimeException()).when(beanA).beanADoSomeThing();
myService.doSomeThing();
}
}
I have an #Aspect that weaves the execution of all my controller action methods. It works fine when I run the system, but not in unit testing. I'm using Mockito and junit in the following way:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:**/spring-context.xml")
#WebAppConfiguration
public class UserControllerTest {
private MockMvc mockMvc;
#Mock
private RoleService roleService;
#InjectMocks
private UserController userController;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
...
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
...
}
with some #Test using mockMvc.perform().
And my Aspect are:
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() { }
#Pointcut("execution(* mypackage.controller.*Controller.*(..))")
public void methodPointcut() { }
#Around("controller() && methodPointcut()")
...
First it is necessary to use webAppContextSetup as Jason suggested:
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() throws Exception {
...
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
At this point the aspect should be triggered but Mockito will not inject mocks. This is because Spring AOP uses a proxy object and the mocks are being injected to this proxy object instead of the proxied object. To fix this it is necessary to unwrap the object and use ReflectionUtils instead of #InjectMocks annotation:
private MockMvc mockMvc;
#Mock
private RoleService roleService;
private UserController userController;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
UserController unwrappedController = (UserController) unwrapProxy(userController);
ReflectionTestUtils.setField(unwrappedController, "roleService", roleService);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
...
public static final Object unwrapProxy(Object bean) throws Exception {
/*
* If the given object is a proxy, set the return value as the object
* being proxied, otherwise return the given object.
*/
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
Advised advised = (Advised) bean;
bean = advised.getTargetSource().getTarget();
}
return bean;
}
At this point any call to when(...).thenReturn(...) should work properly.
It is explained here: http://kim.saabye-pedersen.org/2012/12/mockito-and-spring-proxies.html
You are probably using Spring AOP, in which case the bean has to be a Spring bean for AOP to work, by not autowiring in the controller it is bypassing the Spring AOP mechanism totally.
I think the fix should be to simply inject in the controller
#Autowired
#InjectMocks
private UserController userController;