I have a doubt with Mockito.
I would want to test this simple class:
public class MyClass{
private UserService userService;
public void deleteUser(){
userService.getAdminUser(1);
userService.deleteUser(0);
}
}
I wrote this simple test:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockitoAnnotations.Mock
private UserService userService;
#Test
public void test(){
MyClass myClass=new MyClass();
myClass.userService=userService;
myClass.deleteUser();
}
}
This test run with no errors.
I await that it didn't compile because there isn't any call to userService method..
Mocks created by Mockito are "smart". They don't do anything when a void method is called. They return null when a method returning an object is called. They return an empty collection when a method returning a collection is called.
If you want to verify that getAdminUser() and deleteUser() have been called, use Mockito.verify().
These two things are explained in the Mockito documentation, points 1 and 2. In particular:
By default, for all methods that return value, mock returns null, an empty collection or appropriate primitive/primitive wrapper value (e.g: 0, false, ... for int/Integer, boolean/Boolean, ...).
you have not added any check to see if userService is used in any way. Adding a verify will do that for you: When to use Mockito.verify()?
I would advice you to read up on how Mockito works in tests, I think you have jumped past some of the fundamentals when it comes to learning the design and how method calls to the mock is treated.
Here is how you would test it with a different set of methods which invokes no annotations. Note that this is TestNG, but adapting it to JUnit 4+ is easy:
import static org.mockito.Mockito.*;
public final class Test
{
private UserService userService;
#BeforeMethod
public void init()
{
userService = mock(UserService.class);
}
#Test
{
final MyClass myClass = new MyClass();
myClass.userService = userService;
myClass.deleteUser();
verify(userService, times(1)).getAdminUser(1);
verify(userService, times(1)).deleteUser(0);
}
}
Note that there is a one-argument only verify() variant, which is exactly equivalent to having times(1) as the second argument. There is also never().
If for instance you wanted to test that the .deleteUser() method was not called with any argument, you'd do:
verify(userService, never()).deleteUser(anyInt());
Related
I have a singleton class (so private constructor) which needs to use a Spring Data repository during initialization. I have one injected as a constructor argument. Roughly:
#Controller
public class MyClass {
#Autowired
private MyClass(MyRepository repo) {
repo.findAll();
}
}
I want to unit test my class, so I need to have a mock repository initialized with mock values and then passed into my class before my class is initialized. How do I write my Mockito mocks in my JUnit test to make this possible?
You don't need Spring; this is an advantage of constructor injection. Just use MyRepository mockRepo = mock(MyRepository.class) and new MyClass(mockRepo).
(Your constructor should be public, by the way. You seem to be making the common mistake of confusing different senses of "singleton"; in the case of DI it simply means that the container only makes a single instance and shares it. Finally, if you only have one constructor you don't need #Autowired.)
Unit test should be independent. It means, we are not using real data from database even call any service from our test file.
Assuming you are using JUni5 and have findAllStudents() in your controller. So your test file approximately like this
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyClassTest {
#Mock
private MyRepository repo;
#InjectMocks
private MyClass controller;
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDataIsExist() {
List<String> expectednames = new ArrayList();
expectedNames.add("Foo");
expectedNames.add("Bar");
Mockito.when(myRepo.findAll()).thenReturn(expectedNames);
List<String> result = controller.findAllStudents();
Assertions.assertNotNull(result);
Assertions.assertEquals(expectednames, result);
}
}
So we are mock all the services we use in controller, then inject to controller itself. Then inside the test method we mock repo.findAll() to return expectedNames so if the controller find that function it will return what mock says to return.
After we call the function, we have to make it sure that the result is according to what we expected.
There is little value in a "unit test" for a #Controller implementation. If you use #WebMvcTest, then you can use #MockBean:
#WebMvcTest
class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MyRepository repository;
#Test
void testSomething() {
mockMvc.perform( ... );
}
}
I defined a spy bean:
#Bean
public IMyService myServiceSpy()
{
return Mockito.spy(new MyServiceImpl());
}
In my test I want to capture the argument the service gets.
Of course if I'll define the service as mock instead of spy, it'll work, but I want to activate service's real method and continue the flow, because I need the return value to be calculated by it.
#Inject
private IMyService myServiceSpy;
#Test
public void myTest()
{
//act
//here invoking some service that will eventually invoke MyServiceImpl.
//assert
ArgumentCaptor<SomeObj> someObjCaptor = ArgumentCaptor.forClass(SomeObj.class);
try
{
Mockito.verify(myServiceSpy, Mockito.atLeastOnce()).create(someObjCaptor.capture());
}
catch(Exception e)
{
Assert.fail();
}
assertEquals("some value" , someObjCaptor.getValue());
The strange thing is that the spy's method is activated again when verify() is called, but this time the method called with NULL parameters.
After that it's failing on assertion
org.mockito.exceptions.base.MockitoException: No argument value was captured! You might have forgotten to use argument.capture() in verify()... ...or you used capture() in stubbing but stubbed method was not called. Be aware that it is recommended to use capture() only with verify()
Examples of correct argument capturing:
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());
I'm using the below to run the tests:
#RunWith(SpringJUnit4ClassRunner.class)
I'm not entirely sure you should be using #Inject to spy over your service.
This has worked fine for me in the past:
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.atLeastOnce;
...
#Spy
private MyServiceImpl myServiceSpy;
#Captor
private ArgumentCaptor<SomeObj> someObjCaptor;
#Test
public void myTest()
{
...
try
{
verify(myServiceSpy, atLeastOnce()).create(someObjCaptor.capture());
SomeObj someObj = someObjCaptor.get();
// Assert anything
}
catch(Exception e)
{
Assert.fail();
}
...
}
#Bean
public MyServiceImpl myServiceImpl()
{
return new MyServiceImpl();
}
The annotations really simplify the code. Once you get used to them, it becomes much simpler to read the code, and to type it in =)
The issue is because of Spring AOP as I found in this article:
https://lkrnac.net/blog/2015/12/mock-spring-bean-v2/
I solved it by creating new class that will invoked the original bean.
The new class will be spied in #Configuration class.
public class MyServiceImplSpy implements IMyServiceImpl
{
#Inject
#Qualifier("myServiceImpl")
private IMyService myService; //the original bean
public String create(SomeObj someObj)
{
return myService.create(someObj);
}
}
I have a class I want to test that has several external dependencies, and a couple internal methods. I would like to write a test for MethodA, but not have Method A's internal call to MethodB to actually exercise MethodB. I'd like to mock/stub MethodB and return something specific instead. Usually I'd use when/thenReturn but it doesn't behave like I expect - it actually jumps into Method B while creating the mock itself.
MyService.java
#Service
public class MyService {
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
public SomeObject methodA() {
// some logic using serviceA.method and serviceB.method that creates "output"
SomeObject someObject = methodB(output);
return someObject;
}
public SomeObject methodB(SomeObject someObject) {
// deep mysteries done here to someObject
return someObject
}
}
MyServiceTest.java
public class MyServiceTest {
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#InjectMocks
private MyService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void methodATest() {
when(serviceA.method()).thenReturn(stuff);
when(serviceB.method()).thenReturn(otherStuff);
// here is what I would like to do
when(myService.methodB()).thenReturn(mockedSomeObject); //<- doesn't work
assertThat(myService.methodA().getSomeObjectProperty())
.isEqualTo("property");
}
}
I've looked at solutions that manually mock the MyService class with Mockito.mock(MyService.class), but (as the above example is obviously contrived) my actual class has quite a few external dependencies and I'd prefer a solution that still allows me to mock the service using #Mock for the #Autowired dependencies and #InitMocks for the class under test, unless it's simply not possible.
I've tried:
Mockito.doReturn(mockedSomeObject).when(myService.methodB(any(SomeObject.class));
but that also steps into MethodB when creating the mock for that method, which shouldn't be happening.
Thanks in advance for the help!
Try Adding #Spy to your InjectMocks and use the object to "expect" them in a slightly different syntax.
import org.mockito.Spy;
#InjectMocks
#Spy
private MyService myService;
And now mock the service call
Mockito.doReturn(mockedSomeObject).when(myService).methodB();
Also change the other mock call to this
Mockito.doReturn(stuff).when(serviceA).method();
Mockito.doReturn(otherStuff).when(serviceB).method();
You need to mark your object as Spy or explicitly create a Spy object for it using MyClass objA=null;
MyClass spy_objA=Powermockito.spy(objA)
doReturn(what_you_want).when(spy_objA).method()
Edit: Can find a similar question you may want to check
How to mock another method in the same class which is being tested?
I am trying to test the following class using Mockito and JUnit :
public class A {
private SomeClass someObject;
private SomeImpClass someImpObject1;
private SomeImpClass2 someImpObject2;
public A(SomeImpClass someImpObject1, SomeImpClass2 someImpObject2){
someObject = makeNewObject(someImpObject1, someImpObject2);
}
public makeNewObject(SomeImpClass1 someImpObject1, SomeImpClass2 someImpObject2){
return new SomeObject(someImpObject1,someImpObject2);
}
public usingSomeObject(){
someObject.doSomething();
}
}
So, I wrote a Unit Test using Mockito and JUnit :
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Mock
SomeImpClass1 someImpObject1;
#Mock
SomeImpClass2 someImpObject2;
#Mock
SomeObject someObject;
#Spy
A a;
#Before
public void setUp() {
when(A.makeNewObject).thenReturn(someObject);
this.A = new A(this.someImpObject1, someImpObject2);
when(someObject.doSomething).thenReturn(something);
}
}
The Issue I am facing here is, although I have stubbed the function makeNewObject to return a Mocked object of SomeClass, the code flow is still going inside the fucntion (makeNewObject) and giving a null exception.
What Am I Doing Wrong ?
I have wasted a day behind this.
Not Very Fluent with Mockito.
You wont be able to achieve what you are aiming for with spying and stubbing.
This is because your aiming at stubbing a method used in a constructor.. but you cannot start stubbing once you created a concrete object and spy it.. can't be done..
I would suggest creating a private class inside the test class which extends your class under test, override the method invoked in the constructor and then use it in your tests:
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#Mock
SomeObject someObjectMock;
A a;
#Before
public void setUp() {
this.a = new MyTest();
}
private class MyTest extends ATest{
#Override
public makeNewObject(SomeImpClass1 someImpObject1, SomeImpClass2 someImpObject2){
return someObjectMock;
}
}
Now you dont need to use spying and stubbing of it also as the overriden method is always returning what you expect in the test.
Using Mockito annotations (MockitoJUnitRunner.class, #InjectMocks and #Mock):
#RunWith(MockitoJUnitRunner.class)
public class TagRepositoryTest {
#InjectMocks
private TagRepository repository;
#Mock
private SetupDetails setupDetails;
....
}
I have the test target class using the injected dependency in the constructor:
public class TagRepository {
private final Collection<Tag> tags;
#Autowired
public TagRepository(SetupDetails setupDetails) {
this.tags = Arrays.asList(
new Tag("name", setupDetails.getSourceId()),
...
);
...
}
And I am currently stubbing the method call in #Setup or inside #Test with when():
when(setupDetails.getSourceId()).thenReturn("1");
This is not working as expected. Mockito seems to only stub the method call after the #InjectMocks TagRepository constructor is called, resulting in a null beeing returned instead of "1".
Is there a way to get the stub ready before the constructor is called (using Mockito annotations)?
The only way I am being able to work around this is trying to control the order Mockito setups this scenario giving up on Mockito annotations:
public void setUp() {
setupDetails = mock(SetupDetails.class);
when(setupDetails.getDbId()).thenReturn("1");
repository = new TagRepository(setupDetails);
}
Indeed this is the case and your "work around" is the way to go.
Some will argue that this is a good practice as your test will not compile when you introduce more members to your class under test, as you'll also add them to the constructor.