Mockito mocking objects from inside local method - java

In my class, dependencies are injected by Spring. During testing, I am using mocks. I am getting null pointer exception when I call sys.getId("abc12345") in the following code. I am wondering how to write a unit test that gets 100% coverage.
Class under test:
public class SystemUT implements SUTIface{
#Inject
private AccountLookupDAO dao;
#Inject
private OrchService service;
public Response perform(Request req){
String sellerId = getId(request.getSeller().getNum());
String buyerId = null;
if(req.getBuyerId){
buyerId = getId(request.getBuyer().getNum())
}
service.execute(Request,sellerId,buyerId)
}
String getId(String num){
PrefAcct prefAcctObj = dao.lookupPrefId(num,Consants.StrArr);
PrefSysOfRecObj sorObj= prefAcctObj.getSysOfRecord();
return sorObj.getId();
}
}
Unit test:
public Class SystemUTTest{
#Mock
SystemUT sys;
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
#Mock
PrefAcct prefAcctObj;
#Mock
PrefSysOfRecObj sorObj;
#Before
public void setup(){
Whitebox.setInternalState(sys, daoMock, serviceMock);
}
#Test
public test getId(){
when(dao.lookupPrefId(any(String.class), any(String[].class))).thenReturn(prefAcctObj);
when(prefAcctObj.getSysOfRecord()).thenReturn(sorObj);
when(sorObj.getId()).thenReturn("185");
assertEquals("185",sys.getId("abc12345"));
}
}

Your problem is that your SystemUT class doesn't have its dependencies injected. You could have Spring do this by using their JUnitRunner, but it's not really a unit test then, since you'd be letting Spring dictate which dependencies get injected. Really, you want to control them, and one way to do that is to transform your class to expose its dependencies via a constructor:
public class SystemUT implements SUTIface{
private final AccountLookupDAO dao;
private final OrchService service;
#Inject
public SystemUT(AccountLookupDAO dao, OrchService service) {
this.dao = dao;
this.service = service;
}
}
This will function identically to your current approach since Spring is able to inject dependencies using a constructor annotated with #Inject. Now, when you instantiate your SystemUT class for test, pass mocked objects for its dependencies:
#Mock
AccountLookupDAO daoMock;
#Mock
OrchService serviceMock;
private SystemUT sys;
#Before
public void setup(){
sys = new SystemUT(daoMock, serviceMock);
Whitebox.setInternalState(sys, daoMock, serviceMock);
}

Related

#InjectMocks is not injecting the the list dependency, which is a #Spy

Servcice.java (class to test)
class Service {
#Autowired
private List<Metric> dependency1;
#Autowired
private Executor dependency2;
}
Metric.java : interface
interface Metric{
public void fetchMetric();
}
class Metric1 implements Metric{
public void fetchMetric() {}
}
class Metric2 implements Metric{
public void fetchMetric() {}
}
ServiceTest.java : (test class)
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Spy
private List<Metric> dependency1;
#Mock
private Executor dependency2;
#InjectMocks // class under test // this has above two as their dependencies.
Service service;
#Mock
private Metric1 metric1;
#Mock
private Metric2 metric2;
#BeforeEach
void setUp() {
// intializing the spy object list with mocks.
this.dependency1 = Arrays.asList(metric1,
metric2
);
}
#Test
void someTest() {
// here in debug mode I can see that `dependency1` as a **spy** and 'dependency1' present in the 'Service' are different, though they should be same.
}
}
Why #InjectMock is not able to Inject a #Spy List dependencies in the Service class object? Am I missing something here.
dependency1 as a spy and dependency1 present as a part of the Service are shown as two different objects on the de-bugger and making the test cases fail. I thought they should be the same.
Should we not initialise the Spies in #BeforeEach method ?
The problem is you use #ExtendWith(MockitoExtension.class) to tell mockito to instantiate your #Mocks #Spy and #InjectMocks. So after Mockito does this you change the field ServiceTest.dependency1 to be a list.
But since Mockito instantiated your CUT with the annotated dependencies, it is not updated when you change the ServiceTest.dependency1.
I would recommend to change Service to have a constructor so you can use the recommended constructor Injection.
That way you can make your test like this:
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Mock
private Executor dependency2;
Service service;
#Mock
private Metric1 metric1;
#Mock
private Metric2 metric2;
#BeforeEach
void setUp() {
// intializing the spy object list with mocks.
service = new Service(Arrays.asList(metric1,metric2), dependency2);
}
}
If you want the list to be a spy, I would do something like this:
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#InjectMocks
private Service service;
#Mock
private Executor dependency2;
#Spy
private List<Metric> dependency1;
#Test
void test() {
// you can define what to return when there is a method call on the spy dependency1
Metric metric1 = mock(Metric.class);
Metric metric2 = mock(Metric.class);
when(dependency1.get(0)).thenReturn(metric1);
when(dependency1.get(1)).thenReturn(metric2);
service.test();
}
}
One way to handle it:
Remove #Spy and use #Mock on dependency1.
And you will need to handle the List w.r.t. the testcase like:
Mockito.doReturn(metric1).when(dependency1).get(ArgumentMatchers.anyString());

Use a real component in a service test

I am testing a service that has an autowired helper component. That component has autowired repo.
In my test, I want to use that component helper, not a mock. And I want to mock the repo for that.
But I can't manage to make it work.
The Service that I test:
#Service
public class ServiceImpl{
#Autowired
private Helper helper;
}
The Helper class that has autowired repo
#Component
public class Helper {
#Autowired
private Repository repo;
}
My test should be like this
#ExtendWith(MockitoExtension.class)
public class ServiceImplTest {
ServiceImpl service;
#Mock
private Repository repoMock;
#InjectMocks
private Helper helper;
}
I'd like better to refactor the whole thing but unfortunately, it's not possible...
Any help welcome.
I would prefer constructor injection over field injection. (read more here)
In this case you classes would look something like this:
#Component
public class Helper {
#Autowired
public Helper(Repository repo) {
this.repo = repo;
}
}
#Service
public class ServiceImpl{
#Autowired
public ServiceImpl(Helper helper) {
this.helper = helper;
}
}
This way you can easily create a real Helper object with a mock Repository object:
ServiceImpl service;
private Helper helper;
#Mock
private Repository repoMock;
#BeforeEach
void init() {
helper = new Helper(repoMock);
service = new ServiceImpl(helper);
}
I finally found a solution, thanks for the help.
#ExtendWith(MockitoExtension.class)
public class ServiceImplTest {
#InjectMocks
ServiceImpl service
#Spy
#InjectMocks
private Helper helper;
#Mock
private Repository repoMock;
#InjectMocks
private Helper helper;
}
That way, the mocked repo is injected in the spy helper, and the helper can be injected in the service.
The #Spy objects are actually instantiated, so if you don't stubb any of its methods, you'll get a "real" object.
Here, the mocked repo is injected in the helper and the helper injected in the service.
If Repository is an interface (and not a concrete class) you can try the following:
#ExtendWith(MockitoExtension.class)
public class ServiceImplTest {
#Spy
#InjectMocks
ServiceImpl service = new ServiceImpl();
#Mock
private Repository repoMock;
#InjectMocks
private Helper helper;
}
Try to load a configuration for your tests which gives priority to a mock repo Tested:
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeTest {
#Configuration
static class ContextConfiguration {
#Bean
public Helper helper() {
return new Helper();
}
#Bean
#Primary
public Repository repoMock() {
Repo repo = Mockito.mock(Repository.class);
Mockito.when(/* Mock your repo */);
return repo;
}
}
#Autowired
private Helper helper;
#Test
public void testMethod() {
// Your test goes here
}
}
Anyway, keep in mind that field autowiring is evil. Switch to constructor dependency injection ASAP.
See also:
https://stackoverflow.com/a/39042441/1199132

Mockito Inject mock into Spy object

I'm writing a test case for a Class which has a 2 level of dependency injection. I use #Spy annotation for the 1 level dependency injection object, and I would like to Mock the 2nd level of injection. However, I kept getting null pointer exception on the 2nd level. Is there any way that I inject the mock into the #Spy object?
public class CarTestCase{
#Mock
private Configuration configuration;
#Spy
private Engine engine;
#InjectMocks
private Car car;
#Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
public class Car{
#Inject
private Engine engine;
public void drive(){
engine.start();
}
}
public class Engine{
#Inject
private Configuration configuration;
public void start(){
configuration.getProperties(); // null pointer exception
}
}
I've also wandered how to inject a mock into a spy.
The following approach will not work:
#Spy
#InjectMocks
private MySpy spy;
But the desired behavior can be achieved by a "hybrid" approach, when using both annotation and manual mocking. The following works perfectly:
#Mock
private NeedToBeMocked needToBeMocked;
#InjectMocks
private MySpy mySpy;
#InjectMocks
private SubjectUnderTest sut;
#BeforeMethod
public void setUp() {
mySpy = Mockito.spy(new MySpy());
MockitoAnnotations.initMocks(this);
}
(SubjectUnderTest here depends on MySpy, and MySpy in its turn depends on NeedToBeMocked).
UPD: Personally, I think that if you have to do such a magic too often, it might be a sign that there is something wrong with dependenicies between your classes and it is worth to perform a little bit of refactoring to improve your code.
The (simplest) solution that worked for me.
#InjectMocks
private MySpy spy = Mockito.spy(new MySpy());
No need for MockitoAnnotations.initMocks(this) in this case, as long as test class is annotated with #RunWith(MockitoJUnitRunner.class).
Mockito cannot perform such a tricky injections as it's not an injection framework. So, you need to refactor your code to make it more testable. It's easy done by using constructor injection:
public class Engine{
private Configuration configuration;
#Inject
public Engine(Configuration configuration) {
this.configuration = configuration;
}
........
}
public class Car{
private Engine engine;
#Inject
public Car(Engine engine) {
this.engine = engine;
}
}
In this case you have to handle the mocking and injection manually:
public class CarTestCase{
private Configuration configuration;
private Engine engine;
private Car car;
#Before
public void setUp(){
configuration = mock(Configuration.class);
engine = spy(new Engine(configuration));
car = new Car(engine);
}
#Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
I also met this issue during the unit testing with Spring boot framework, but I found one solution for using both #Spy and #InjectMocks
Previous answer from Yoory N.
#Spy
#InjectMocks
private MySpy spy;
Because InjectMocks need to have instance created, so the solution works for me is at below,
#Spy
#InjectMocks
private MySpy spy = new MySpy();
I think I just found the definitive answer. I tried Yoory approach but changed the order of the annotations :
#InjectMocks
#Spy
private MySpy spy;
I assume that Mockito first creates the mock, and adds a spy on top of that. So there is no need to instantiate the MySpy object.
Junit5 + mockito-junit-upiter-4.2.0 works well
#ExtendWith(MockitoExtension.class)
public class MyTest {
#Spy
#InjectMocks
private SomeClass spy = new SomeClass();

Null pointer exception on #PostConstruct method?

I'm developing a small application in Java using Spring, so I have this Service:
public class AccountService implements UserDetailsService {
#Autowired
private AccountRepository accountRepository;
#Autowired
private BlogRepository blogRepository;
#Autowired
private ImageService imageService;
#PostConstruct
protected void initialize() throws IOException {
Account user = new Account("user", "demo", "ROLE_USER");
save(user);
Blog userBlog = new Blog("userBlog", true, user);
userBlog.setAvatar(imageService.createBlogAvatar(userBlog.getName()));
blogRepository.save(userBlog);
save(new Account("admin", "admin", "ROLE_ADMIN"));
}
// More methods
}
And this test:
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
#InjectMocks
private AccountService accountService = new AccountService();
#Mock
private AccountRepository accountRepositoryMock;
#Test
public void shouldInitializeWithTwoDemoUsers() throws IOException {
// act
accountService.initialize();
// assert
verify(accountRepositoryMock, times(2)).save(any(Account.class));
}
}
Why when I run the tests I get this exception?
shouldInitializeWithTwoDemoUsers(es.udc.fi.dc.fd.account.AccountServiceTest) Time elapsed: 0.016 sec <<< ERROR!
java.lang.NullPointerException: null
at es.udc.fi.dc.fd.account.AccountService.initialize(AccountService.java:45)
at es.udc.fi.dc.fd.account.AccountServiceTest.shouldInitializeWithTwoDemoUsers(AccountServiceTest.java:42)
Using the #PostConstruct annotation it's supposed to have all beans injected right?
Few things here. First of all #InjectMocks generally makes things easier but Mockito not a dependency injection framework, so its not guaranteed to work properly.
Secondly, for #InjectMocks to work properly you need to #Mock all your objects as well and not manually create the class you are trying to inject. I don't believe its the case anymore but in order versions of mockito, the order of the #Mocks would matter as well.
This code might work for you
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
#Mock
private AccountRepository accountRepositoryMock;
#Mock
private BlogRepository blogRepository;
#Mock
private ImageService imageService;
#InjectMocks
private AccountService accountService ;
#Test
public void shouldInitializeWithTwoDemoUsers() throws IOException {
// act
accountService.initialize();
// assert
verify(accountRepositoryMock, times(2)).save(any(Account.class));
}
}
You need to mock all the dependencies that your test subject is using. You may want to do this in your AccountServiceTest class:
#Mock
private BlogRepository blogRepositoryMock;

Mockito - Creating nested mock objects

I'm learning Mockito. I am facing problem while creating mock for nested objects. See
public interface BaseManager {
public Query createQuery(String queryString);
}
and an implementation class for that
public class BaseManagerImpl implements BaseManager {
#Autowired
private SessionFactory sessionFactory;
// ...
}
Module level hibernate manager, for example:
public interface RegistrationManager {
#Transactional
public List<Country> getCountries();
}
and an implementation class for that
public class RegistrationManagerImpl implements RegistrationManager {
#Autowired
private BaseManager baseManager;
// ...
}
Now I'm facing problem in creating mocked base manager. My test class is:
public class MockitoTest {
private RegistrationManager registrationManager = new RegistrationManagerImpl();
#Mock private BaseManager baseManager;
#Mock private SessionFactory sessionFactory;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
// baseManager.setSessionFactory(sessionFactory);
registrationManager.setBaseManager(baseManager);
}
// ...
// #Test
}
Problem: sessionFactory is not instantiated inside baseManager.
Please help creating mock object. Thanks in advance!
The problem is that you are creating a mock of BaseManager but only BaseManagerImpl has a SessionFactory field. Mockito doesn't know about BaseManagerImpl. In your code you create two mocks which are completely independent of each other.
Unit tests are about testing an unit. So you should test BaseManagerImpl and RegistrationManagerImpl separately.
So you test BaseManagerImpl first:
public class BaseManagerImplTest {
private BaseManagerImpl target;
// ...
}
then you test RegistrationManagerImpl:
public class RegistrationManagerImplTest {
private RegistrationManagerImpl target;
// ...
}
I suggest that you should use the name target or something similar for your test target in your test class becaues it will make your code much more easier to read.
Another thing: If you test an object all of its dependencies should be mocked but you shouldn't care about the mocks' dependencies. You just mock their method invocations like:
Mockito.when(myMock.someMethod()).thenReturn(someResultObject);
You have to put the #InjectMocks annotation before class you want to test and mock the methods which are called by the basemanger or sessionFactory.
public class MockitoTest {
#InjectMocks
private RegistrationManager registrationManager = new RegistrationManagerImpl();
#Mock private BaseManager baseManager;
#Mock private SessionFactory sessionFactory;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
// baseManager.setSessionFactory(sessionFactory);
registrationManager.setBaseManager(baseManager);
Mockito.when(baseManager.yourMethod()).thenReturn(someObject);
}
// ...
// #Test
}
Hope this is it you're looking for!
You cannot inject a mock of SessionFactory into a mock of BaseManager.
As you are testing RegistrationManagerImpl, you just need to have a mock of BaseManager. You can use method stubbing so that the methods BaseManager will return the stubbed values when those methods are called from RegistrationManagerImpl methods. So, if you have a RegistrationManagerImpl as this:
public class RegistrationManagerImpl implements RegistrationManager {
#Autowired
private BaseManager baseManager;
// ...
public String doSomething(){
return baseManager.process();
}
}
you can write your MockitoTest as this:
public class MockitoTest {
#InjectMocks
private RegistrationManager registrationManager = new RegistrationManagerImpl();
#Mock private BaseManager baseManager;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
// ...
#Test
public void test() {
when(baseManager.process()).thenReturn("hello");
assertEquals("hello", registrationManager.doSomething());
}
}
And while testing BaseManager, there you need to use mock of SeesionFactory.

Categories