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);
Related
I am looking to Mock my service and all the autowired fields in it. Below is the service:
#Service
public class MyServiceImpl implements MyService {
#Autowired
#Qualifier("propvalues")
Map<String,String> propvalues;
...
...
}
Below is the Configuration class
#Configuration
public class MyValuesConfig {
#Bean(name = "propvalues")
#ConfigurationProperties(prefix = "mysvcvalues")
private Map<String,String> propvalues;
}
The configuration class reads from application-myvalues.yml
This is my Test class MyServiceImplTest
#ExtendWith(MockitoExtension.class)
public class MyServiceImplTest {
#Mock
private MyService myService;
...
...
}
Now I find that the autowired fields in the mock service is null. Ideally I would want to see the values loaded from the configuration in the Map in the mock service.
Instead of #Mock user #SpyBean
#SpyBean inject object like an actual bean but you can mock any of its parts separately.
Or you can use spy to initialize the object and mock the other parts that you want.
Something like:
Mokito.spy(myService).when(....).thenReturn(...)
Is it possible to somehow have in the same test class #MockBean and #Autowired of the same service?
In other words, I would like to have #MockBean service only for one test, while for others tests of the same class I need it as #Autowired.
This relies on the difference between #MockBean and #Autowired.
#Autowired
only does a lookup in the SpringContext for a bean of that type. This means that you will need to create that bean if you need to 'autowire' it
#MockBean
does exactly what you expect from the name, it creates a 'mock' of the service, and injects it as a bean.
so this
class MyTest {
#MockBean
MyService myService;
}
is equivalent to this
#Import(MyTest.Config.class)
class MyTest {
#Autowired
MyService myService;
#TestConfiguration
static class Config {
#Bean
MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
So, if you need to have a different bean of the MyService type in other tests, you need to create the bean in a #TestConfiguration annotated class
#Import(MyTest.Config.class)
class MyTest {
#Autowired
MyService myService;
#TestConfiguration
static class Config {
#Bean
MyService myService() {
return new MyServiceImpl();
}
}
}
Or, in a class annotated with #Configuration
#Import(MyConfig.class)
class MyTest {
#Autowired
MyService myService;
}
#Configuration
public class MyConfig {
#Bean
MyService myService() {
return new MyServiceImpl();
}
}
The best solution is to change #MockBean to #SpyBean. And in the method you will be able to do like this:
kotlin
#SpyBean
lateinit var serviceMock: Service
#Test
fun smallTest()
`when`(serviceMock.doSomething())
.thenReturn(false)
// your test logic
}
I suspect that the source of the evil here is field injection.
Olvier Gierke (now Drotbohm) wrote a blog post about why field injection is evil.
If you can switch to constructor injection you can mock the service just in your test and pass the mock to the class you want to test.
I just want to leave this answer here as a suggestion for others who might have the chance to use constructor injection instead.
Running a test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
This error gets thrown only with unit tests, the program itself works.
My Interface
public interface MyService {
public String testMethod();
}
My Implementation
#Service
public class MyServiceImpl implements MyService{
#Autowired
private TransactionRepository transactionRepo;
#Autowired
private AccountRepository accountRepository;
#Autowired
private BankAccountStatementFactory baStatementFactory;
public String myMethod() {
return "Run My Method";
}
}
My Unit Test
#RunWith(SpringRunner.class)
#SpringBootTest
public class DeleteMeTest{
#Mock
private TransactionRepository transactionRepo;
#Mock
private AccountRepository accountRepository;
#Mock
private BankAccountStatementFactory baStatementFactory;
#InjectMocks
#Resource
MyServiceImpl myService;
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myService.myMethod();
System.out.println("My Unit Test");
}
}
Running this test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
A solution here is to inject the Interface, not the implementation into the unit test but this will not allow me to inject mocks.
Thats because an implementation is required with the #InjectMocks annotation. When I try to inject mocks into the interface I get the following exception:
Cannot instantiate #InjectMocks field named 'myService'! Cause: the type 'MyService' is an interface.
Just to be clear, all this worked in the beginning and went bad once I repackaged my classes. That could be the cause but not 100% sure.
Any hints on what might cause this BeanNotOfRequiredTypeException?
Thanks!
Since it's a unit test, you don't need Spring IMHO.
Simply initialize the tested class this way:
#InjectMocks
MyServiceImpl myService = new MyServiceImpl();
You can also remove these annotations:
#RunWith(SpringRunner.class)
#SpringBootTest
If you really need to use Spring (the reason for using Spring in the unit test is not clear from your post), you can try to unproxy the bean:
Have a separate declaration for the proxy and the bean:
#Resource
MyServiceImpl proxy;
#InjectMocks
MyServiceImpl myService;
Then initialize them in setUp():
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
myService = (MyServiceImpl)((TargetSource) proxy).getTarget();
MockitoAnnotations.initMocks(this);
}
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.
I'm trying to provide a clean Unit Test for a Controller of mine. This Controller has a Service as dependency and this Serviceh has a Datasource as dependency.
The test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ContentActionWebServiceControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private MyService myService;
#Test
public void getRequestActionList() throws Exception {
when(...)
perform(...);
verify(...);
}
#Configuration
#ImportResource("...")
static class MyTestConfiguration {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
And the MyService is something like
#Service
public class MyService {
#Autowired
private MyDataSource myDatasource;
...
}
Because MyService as an Autowired property MyDataSource, the context isn't initialized because it doesn't find any MyDataSource type for satisfying the #Autowired annotation of MyService. But why does it ever try to resolve this annotation? Is this is a mock?
Mockito does use cglib to create a new child class of MyService (and override all methods with mock methods).
But still, the dependencies of the parent will be injected, because this is how Spring does it's job:
if you have a parent class with some #Autowired fields, and a child class that inherits from this parent class, then Spring will inject the #Autowired fields of the parent when instantiating the child. I guess it's the same behavior in your case.
If you use an interface for MyService, then your problem will be solved.
If it's supposed to be a unit test (and not an integration test) you don't even need to use Spring, you can do it all with JUnit+Mockito. Rather than #Autowireing dependencies from Spring context, you can simply create mocks of the support objects (via #Mock) and inject them to the testee (via #InjectMocks). I believe your code could be simplified to something (conceptually) like this:
#RunWith(MockitoJUnitRunner.class)
public class ContentActionWebServiceControllerTest {
#Mock
private Service mockServiceUsedByController;
#InjectMocks
private YourController testee;
#Test
public void getRequestActionList() throws Exception {
assertFalse(testee.getRequestActionList().isEmpty());
// etc.
}
}