Use a real component in a service test - java

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

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

Can't inject Bean (child of #Spy injected bean) in my Test

When I'm making a Test, I can't get injected a property of one of the injected beans (with #Spy). I am using Mockito to test.
I tried using #Mock, #Spy, #SpyBean and #InjectMocks in this Bean on my test but I can't get it injected.
#RunWith(MockitoJUnitRunner.class)
public class MyTest{
#InjectMocks private MyService = new myService();
#Spy private MyFirtsDepen firstDepen;
#Autowired #Spy private ChildDepen childDepen;
... More mocks and tests
}
#Service
public class MyService {
#Autowired private MyFirstDepen firstDepen;
....
}
#Mapper
public class MyFirstDepen {
#Autowired private ChildDepen childDepen;
....
}
#Component
public class ChildDepen {
...
}
When my test use firstDepen is working great, but when firstDepen uses childDepend always get Nullpointer. How can I inject this property in my test?
Since your MyFirtsDepen is a mock, there is no way to inject anything to it. Configure mock to return another mock.
when(firstDepen.getChildDepen()).doReturn(childDepen);

Not able to use #Autowired with #Mock while creating unit test cases

I have a service say MainService and it has few managers which are initialised with #Autowired, and it is using some external service which are also #Autowired.
My purpose is to create unit test cases so that I can access the inmemory DB with managers, and want to mock the external service.
Now problem which I am facing is if I use #Autowired in my unit test and use #Mock for external services, then it doesn't use mock methods, Instead it uses the actual implementation. If I do #InjectMocks then it doesn't pick the data from repo as it doesn't find the respective dependencies for managers, and if I use #Autowired and #InjectMocks together it still not being able to use the Mocks.
Something like this
#Service
public class MainService extends AbstractService
{
#Autowired
Manager1 manager1;
#Autowired
Manager2 manager2;
#Autowired
Manager3 manager3;
#Trace(dispatcher = true)
public void mainMethod(int data)
{
int data1 = manager1.getData(int xyz);\\ getting data from DAO
int data2 = manager1.getData(int xyz);\\ getting data from DAO
int data3 = manager1.getData(int xyz);\\ getting data from
\\External Service
}
}
Now the test case I am writing is
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class TestClass {
#InjectMocks
#Autowired
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
Manager3 manager3;
#Before
public void initTest()
{
MockitoAnnotations.initMocks(this);
int dataFirst=1;
int dataSecond =2;
int dataThird=3;
int dataForMethod=4;
repoManager1.save(dataFirst);
repoManager2.save(dataSecond);
}
#Test
public void testMethod()
{
Mockito.when(manager3.getData(Mockito.anyInt())).thenReturn(dataThird);
service.mainMethod(dataForMethod);
}
}
This is a replication of the actual service, when I debug the test I found that the mock is not being used, its using actual implementation, and when I removed #Autowired from MainService then it only execute the mocked method.
Instead of using Field Injection(using #Autowired on class variables) use Constructor Injection. This way you can initialize your MainService class with some mocks and some real Implementations. Something like this
#Service
public class MainService extends AbstractService
{
private final Manager1 manager1;
private final Manager2 manager2;
private final SomeExternalService externalService;
#Autowired
public MainService(Manager1 manager1, Manager2 manager2, SomeExternalService externalService)
this.manager1= manager1;
this.manager2= manager2;
this.externalService = externalService;
}
........................
}
From your test class don't Autowire MainService. Just Autowire Manager1 and Manager2 and Create Mock for SomeExternalService(and initialize it). And then create instance of MainService using constructor.
public class TestClass {
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
SomeExternalService externalService;
#Before
public void setUp(){
service = new MainService(repoManager1, repoManager2, externalService);
}
}
If you want to use Mockito, you need Annotate your TestClass with #RunWith(MockitoJUnitRunner.class) instead of #RunWith(SpringRunner.class).
Then for the #AutoWired in the TestClass for repoManager1, repoManger2. Annotate them with #Mock instead of #Autowired, as you want to mock them with Mockito.
I have not used the SpringRunner myself, but from a quick read, I can see that it is used mostly for Integration Test, where you want to load the SpringContext..etc.
Also #ActiveProfiles("test") is mostly used for Integration Test, where you want to load the spring context, with the 'test' profile properties.

Mockito mocking objects from inside local method

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

How to mock a private dao variable?

I have a dao.create() call that I want to mock when testing a method.
But I am missing something as I'm still getting NPE. What is wrong here?
class MyService {
#Inject
private Dao dao;
public void myMethod() {
//..
dao.create(object);
//
}
}
How can I mock out the dao.create() call?
#RunWith(PowerMockRunner.class)
#PrepareForTest(DAO.class)
public void MyServiceTest {
#Test
public void testMyMethod() {
PowerMockito.mock(DAO.class);
MyService service = new MyService();
service.myMethod(); //NPE for dao.create()
}
}
You are not injecting the DAO. With mockito you can change your test class to use #InjectMocks and use mockito runner.
#RunWith(MockitoJUnitRunner.class)
public void MyServiceTest {
#Mock
private Dao dao;
#InjectMocks
private MyService myService;
...
}
You can read more about InjectMocks at Inject Mocks API
Simpler way is changing your injection to injection by constructor. For example, you would change MyService to
class MyService {
...
private final Dao dao;
#Inject
public MyService(Dao dao) {
this.dao = dao;
}
...
}
then your test you could simple pass the mocked DAO in setup.
...
#Mock
private Dao dao;
#Before
public void setUp() {
this.dao = mock(Dao.class);
this.service = new MyService(dao);
}
...
now you can use verify to check if create was called, like:
...
verify(dao).create(argThat(isExpectedObjectBeingCreated(object)));
}
private Matcher<?> isExpectedObjectBeingCreated(Object object) { ... }
Using injection by constructor will let your dependencies clearer to other developers and it will help when creating tests :)
You still need to set the dao field with your mock.
You can use reflection to this.
You need to inject/set the mocked object DAO in your service class.
If it is a spring based project, you may have a look # Spring Junit Testrunner
If you use new MyService() the Dao is never injected. For the Dao to be injected you need to load the MyService via an ApplicationContext (Spring) or an Injector (Guice). Like you would in your normal application.
As others have already said, you need to set the dao field in your MyService class in some fashion. I'm unsure the mechanism to allow for a compound runner on your test to use both Powermock and a DI framework runner (assuming Powermock is required), but as long as you're already using PowerMock (for reasons unclear in the given example), you could avail yourself of the Whitebox class to set the dao more manually.
public void testMyMethod() {
Dao dao = mock(Dao.class)
doNothing().when(dao).create(anyObject())); //assuming no return val for dao.create()
MyService service = new MyService();
Whitebox.setInternalState(service, "dao", dao);
service.myMethod();
}

Categories