Using it to test in Spring.
Using #Mock Mockito is necessary to overwrite all methods replies of the original class with when clauses? And what is the difference with #MockBean ?
Its not accessory to override overwrite all methods.
#Mock is pure Mockito library where #MockBean is spring implementation of Mockito
So in your spring component class if not defined #Autowired fields (Just like simple java class to mock as object) then use #Mock annotation to mock the object of that class.
But your sping component class has #Autowired fields and you want spring container initialize that fields as Mock object then use #MockBean annotation (spring will do mock and autowire it).
For ex
class Test {
public void testMethod(){ -- some code --}
}
The you can use #Mock annotation for Test call to mock it. There is no other dependencies or fields in Test class.
#Component
class Test {
#Autowired
Other other;
public void testMethod(){ -- some code --};
}
Then you can mock Other object using #MockBean if you want to mock it and pass by the functionality of other (i.e we mock DAO layer and not calling actual logic). Spring container will initialize that other field.
Related
I have a very bad bean implementation
#Component
public class Repository{
public List<String> people= new ArrayList<>();
And I also have a test where I replace the repository with a mock.
But when I try to access the "people" field in the test via the repository mock i get NullPointerException
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MainControllerTest{
#Autowired
private MockMvc mockMvc;
#MockBean
private Repository repository;
#Test
public void someTest(){
repository.people // null -> NullPointerException
}
}
Why it happens? Is the bean initialized initially or not? What's the correct solution with such a bad implementation?
Bean initialized as it suppose to be with #MockBean.
It produces just a stub for you. All nested stuff of the Repository class is ignored.
In your case you have a completely initialized mock. But of course it's nested fields are null. Becase it is a mock and nested fields will not be initialized. Mock assumes that you just need kind of wrapper of that particular class to mock some behavior on that.
What you need to do is to change #MockBean to the #SpyBean.
In that case your class fields would be initialized as it defined in your Repository class. So, you will have a real Repository object but wrapped with Mockito Spy.
And it will allow you to perform all testing manipulations on that object that needed.
I am unable to pass the field by reading from application-test.properties file from test to the mocked method.
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
public class ReportImplTest {
#Mock
private Dependencies dependencies;
#InjectMocks
private ReportImplTest underTest;
#Test
public void testgetReports() {
List<String> reps= underTest.getReports(anyString());
}
}
Here is the actual class of the mocked method
#Component
public class ReportImpl {
#Value("${REP_PROPNAME}")
String reppropname;
public List<String> getReports(String rep){
return staticUtilityclass.process(staticUtilityclass.transform(reppropname,"Reports"));
}
}
reppropname is coming as null in the getReports method. Test is executing in test context wheres the ReportImpl class will be in application context. Is there a way to get the value of the reppropname.
I tried used #ContextConfiguration (#ContextConfiguration(classes={ApplicaitonBootStarter.class)}
it is working , but it loads all the beans and dependencies.
Any other way to get the reppropname?
The reason why the value is not injected here is that you don't provide the configuration to your test class. Spring just doesn't know how to build your bean.
So, as you mentioned you have to annotate the test class with #ContextConfiguration. If you don't want to build the entire context with all the beans, you can provide create a test configuration and provide there only the needed beans.
#Configuration //can be as well annotated with #TestConfiguration
#ComponentScan("package.to.scan")
public class TestConfiguration {
}
And now provide this class to your test
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
#ContextConfiguration(classes = TestConfiguration.class)
public class ReportImplTest {
........
}
But there is one more thing. Assuming that you have a #Before method that performs MockitAnnotations.initMocks(this);, you still have your object-under-test declared only with #InjectMocks. What does it mean? It means that if you don't initialize this object by yourself, mockito will take care of it and will initialize with using the available constructor, and in this case, spring won't inject the #Value annotated field. What you need to do, is to annotate you object-under-test with #Autowired so spring will initialize it before mockito will try to take care of it:
#InjectMocks
#Autowired
private ReportImplTest underTest;
I have a service class that I need to unit test. The service has a upload method which in turn calls other services(autowired beans) that updates the database. I need to mock some of these services and some to execute as it is.
#Service
public class UploadServiceImpl implements UploadService{
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}
In the above example I need to mock ServiceA, but i would like ServiceB to run as is and perform it's function.
My Junit test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes=Swagger2SpringBoot.class)
public class UploadServiceTest {
#Mock
private ServiceA serviceA;
#InjectMocks
private UploadServiceImpl uploadService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpload(){
uploadService.upload();
}
When I execute this I get NPE at serviceB.execute(); in UploadServiceImpl.
What could be the problem?
Note: I am not specifying the behavior of the mocked object because I don't really care and also default behavior of mocked objects are to do nothing.
Thanks!
Usually when unit testing you want to mock all external dependencies of a class. That way the unit test can remain independent and focused on the class under test.
Nevertheless, if you want to mix Spring autowiring with Mockito mocks, an easy solution is to annotate with both #InjectMocks and #Autowired:
#InjectMocks
#Autowired
private UploadServiceImpl uploadService;
The net effect of this is that first Spring will autowire the bean, then Mockito will immediately overwrite the mocked dependencies with the available mocks.
The issue you are facing is due to the use of #InjectMocks annotation.#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
So in your case when trying to inject mocks only one mock bean is present and the other bean ServiceA is not getting injected.To solve this issue :
You can try not using #InjectMocks at all instead pass a mock object for the method that you want to mock while pass rest of the autowired objects into the constructor.Example :
Here to test i am passing one mock object and one autowired object.
#RunWith(MockitoJUnitRunner.class)
public class SampleTestServiceImplTest {
#Mock
private SampleClient sampleClient;
#Autowired
private BackendService backendService ;
private BackendServiceImpl backendServiceimpl;
#Before
void setUp() {
backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
}
Or another way you can make this work is by using #Autowired annotation along with the #InjectMocks.#Autowired #InjectMocks are used together and what it will do is inject the mocked class and Autowired annotation adds any other dependency which the class might have.
Answer referred from : https://medium.com/#vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29
In my opinion, we are writing unit test cases and we should not initialize the spring context in order to test a piece of code.
So,
I used Mockito to mock the Autowired beans in my main target test class and injected those mock beans in my main test class Object
maybe sounds confusing, see the following example 💥
Dependencies I used
testImplementation("org.mockito:mockito-core:2.28.2")
testImplementation("org.mockito:mockito-inline:2.13.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")
My main class is Maths and Calculator bean is autowired
class Maths{
#Autowired Calculator cal;
.........
.........
public void randomAddMethod(){
cal.addTwoNumbers(1,2); // will return 3;
}
}
Test class
#ExtendWith(MockitoExtension.class)
class MathsTest{
#Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;
#InjectMocks Maths maths = new Maths();
#Test testMethodToCheckCalObjectIsNotNull(){
maths.randomAddMethod();
}
}
Now cal will not be null in Maths class and will work as expected
Add
#Mock
private ServiceB serviceB;
to create injectable mock of missing service just like you did with service A.
In my case, beside using combination of #InjectMocks and #Autowired, I also had to provide setter for the mocked object in the tested class (setter for ServiceA in UploadServiceImpl in the original example). Without that the real method of ServiceA was called.
Another way is to define an autowired constructor so that you can test the services properly.
#Service
public class UploadServiceImpl implements UploadService{
private ServiceA serviceA;
private ServiceB serviceB;
#Autowired
public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}
I couldn't make it work without using ReflectionTestUtils. Setting the constructor is one option if it's viable for you.
I have a Spring service with multiple field dependencies as below. One of the dependency (thirdPartyService) communicates with an external application. How can I just mock that?
#Service
public class PlannerServiceImpl implements PlannerService {
private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);
#Autowired
private RepositoryA repositoryA;
#Autowired
private RepositoryB repositoryB;
#Autowired
private ThirdPartyService thirdPartyService ;
}
If I use Mock annotation then it still connects to external application instead of returning mock response:
#Mock
ThirdPartyService thirdPartyService;
#Autowired
PlannerService plannerService;
And If I use InjectMocks annotation then it gives NullpointerException for RepositoryA and RepositoryB.
#Mock
ThirdPartyService thirdPartyService;
#InjectMocks
PlannerService plannerService = newPlannerService();
How can I just Mock ThirdPartyService and let Spring inject other dependencies?
You can modify what Spring has injected using Whitebox. Optionally, since you are using Spring, you can also use ReflectionTestUtils.setField
After Spring injects the dependencies, and before your unit test runs, you can use org.powermock.reflect.Whitebox to modify Spring's injection. Something like this
Whitebox.setInternalState(plannerService, "thirdPartyService" thirdPartyService);
Where thirdPartyService is your mocked instance.
javadoc here
or using Spring's ReflectionTestUtils:
ReflectionTestUtils.setField((plannerService, "thirdPartyService" thirdPartyService);
java doc here
This can typically be done in your "setup" method, the one annotated with #Before.
You can have a setter method in your PlannerService class under test
void setRepositoryA(RepositoryA repository) {
this.repositoryA = repository;
}
And then use this method in your test to supply a mock implementation of the RepositoryA
Or you can #Inject the repository in the constructor and then in your unit test call the constructor with the mocks as the arguments, instead.
The #Autowiring and #InejctMocks can be used in a test case for sure (annotations being used separately on different instance fields).
Make sure you:
1) Initiate Mockito mocks in the #Before method:
#Before
public void before(){
MockitoAnnotations.initMocks(this);
}
2) Use SpringJUnit4ClassRunner.class in the #RunWith class annotation
We have a project that uses Spring annotations to configure its context.
To test this project we are using Mockito and it's Spring extension.
In tests I need to override some beans with mock/spy version.
With the #Mock/#Spy and #InjectMock annotations I have been able to use spy for beans using autowiring mechanism.
Now I have a third party component which create another Spring context and then merge the 2 contexts together. This third party component retrieve beans using a call to the context:
applicationContext.getBean(key);
In this case, the #Mock/#Spy and #InjectMock combination is not working.
The 'workaround' solution I have put in place is an XML file in which I declare my spied bean:
<mockito:spy beanName="beanToSpy"/>
To stay in the annotation world, I have tried springockito-annotations as mentioned in these similar questions:
Injecting Mockito mocks into a Spring bean
and its duplicate:
How to inject a Mock in a Spring Context
But the bean is not spied or mocked, I've probably a configuration error.
My current setup is:
A base class that is in charge of the Spring config for test:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#ContextConfiguration(loader= SpringockitoContextLoader.class, locations ={"/config.xml","/test-config.xml"})
public abstract class BaseTest {
//...
}
A test class that would like to use a mocked bean:
public class MyTest extends BaseTest {
#ReplaceWithMock #Autowired MyService myService;
#WrapWithSpy #Autowired OtherService otherService;
#Test public void someTest() {
doReturn(x).when(myService).call();
doReturn(y).when(otherService).process();
}
}
Unfortunately in MyTest, the beans are not replaced by their mock/spy versions, it is the plain old regular version.
EDIT:
The third party component that does the lookup is using its own spring parent context and add the current spring context into its own. The lookup to retrieve the component that I want to be mocked occurs after the context has been fully loaded.
What should I do to properly replace the bean in the context with a mock/spy version ?
What is wrong with the way I'm using #WrapWithSpy / #ReplaceWithMock ?
When does the call to
applicationContext.getBean(key);
happens? Is it possible that it retrieves the bean before the SpringockitoContextLoader has a chance to replace it with a spy?
One solution to stay in annotation world would be to override the bean in java config:
#Bean
public MyBeanType myBeanType() {
return spy(new MyBeanType(...))
}
The more conventional way to perform this is by simply mocking them in the test as required :
public class MyTest extends BaseTest {
#Test public void someTest() {
MyService myMockService = Mockito.mock(MyService.class);
// define when's
// perform verification
}
You can inject using SpringReflectionTestUtils, or if using setter injection just set normally.
If you use a global mocked bean in a test class with multiple tests, the results can get confusing.