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);
}
}
Related
I am developing a Spring Boot application. For my regular service class unit tests, I am able to extend my test class with MockitoExtension, and the mocks are strict, which is what I want.
interface MyDependency {
Integer execute(String param);
}
class MyService {
#Autowired MyDependency myDependency;
Integer execute(String param) {
return myDependency.execute(param);
}
}
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#Mock
MyDependency myDependency;
#InjectMocks
MyService myService;
#Test
void execute() {
given(myDependency.execute("arg0")).willReturn(4);
myService.execute("arg1"); //will throw exception
}
}
In this case, the an exception gets thrown with the following message (redacted):
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'execute' method:
myDependency.execute(arg1);
- has following stubbing(s) with different arguments:
1. myDependency.execute(arg0);
In addition, if the stubbing was never used there would be the following (redacted):
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at MyServiceTest.execute()
However, when I use #MockBean in an integration test, then none of the strict behavior is present. Instead, the stubbed method returns null because the stubbing "fails" silently. This is behavior that I do not want. It is much better to fail immediately when unexpected arguments are used.
#SpringBootTest
class MyServiceTest {
#MockBean
MyDependency myDependency;
#Autowired
MyService myService;
#Test
void execute() {
given(myDependency.execute("arg0")).willReturn(4);
myService.execute("arg1"); //will return null
}
}
Is there any workaround for this?
Yes there are some workarounds but it is quite involved.
It may be better to just wait for Mockito 4 where the default will be strict mocks.
The first option:
Replace #MockBean with #Autowired with a test configuration with #Primary ( this should give the same effect as #MockBean, inserting it into the application as well as into the test )
Create a default answer that throws an exception for any unstubbed function
Then override that answer with some stubbing - but you have to use doReturn instead of when thenReturn
// this is the component to mock
#Component
class ExtService {
int f1(String a) {
return 777;
}
}
// this is the test class
#SpringBootTest
#RunWith(SpringRunner.class)
public class ApplicationTests {
static class RuntimeExceptionAnswer implements Answer<Object> {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
throw new RuntimeException(
invocation.getMethod().getName() + " was not stubbed with the received arguments");
}
}
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public ExtService mockExtService() {
ExtService std = Mockito.mock(ExtService.class, new RuntimeExceptionAnswer());
return std;
}
}
// #MockBean ExtService extService;
#Autowired
ExtService extService; // replace mockBean
#Test
public void contextLoads() {
Mockito.doReturn(1).when(extService).f1("abc"); // stubbing has to be in this format
System.out.println(extService.f1("abc")); // returns 1
System.out.println(extService.f1("abcd")); // throws exception
}
}
Another possible but far from ideal option: instead of using a default answer is to stub all your function calls first with an any() matcher, then later with the values you actually expect.
This will work because the stubbing order matters, and the last match wins.
But again: you will have to use the doXXX() family of stubbing calls, and worse you will have to stub every possible function to come close to a strict mock.
// this is the service we want to test
#Component
class ExtService {
int f1(String a) {
return 777;
}
}
// this is the test class
#SpringBootTest
#RunWith(SpringRunner.class)
public class ApplicationTests {
#MockBean ExtService extService;
#Test
public void contextLoads() {
Mockito.doThrow(new RuntimeException("unstubbed call")).when(extService).f1(Mockito.any()); // stubbing has to be in this format
Mockito.doReturn(1).when(extService).f1("abc"); // stubbing has to be in this format
System.out.println(extService.f1("abc")); // returns 1
System.out.println(extService.f1("abcd")); // throws exception
}
}
Yet another option is to wait until after the test finishes using the mock, and then use
verifyNoMoreInteractins();
As mentioned in this comment, this GitHub issue in the spring-boot project addresses this same problem and has remained open since 2019, so it's unlikely that an option for "strict stubs" will be available in #SpringBootTest classes anytime soon.
One way that Mockito recommends to enable "strict stubs" is to start a MockitoSession with Strictness.STRICT_STUBS before each test, and close the MockitoSession after each test. Mockito mocks for #MockBean properties in #SpringBootTest classes are generated by Spring Boot's MockitoPostProcessor, so a workaround would need to create the MockitoSession before the MockitoPostProcessor runs. A custom TestExecutionListener can be implemented to handle this, but only its beforeTestClass method would run before the MockitoPostProcessor. The following is such an implementation:
public class MyMockitoTestExecutionListener implements TestExecutionListener, Ordered {
// Only one MockitoSession can be active per thread, so ensure that multiple instances of this listener on the
// same thread use the same instance
private static ThreadLocal<MockitoSession> mockitoSession = ThreadLocal.withInitial { null };
// Count the "depth" of processing test classes. A parent class is not done processing until all #Nested inner
// classes are done processing, so all #Nested inner classes must share the same MockitoSession as the parent class
private static ThreadLocal<Integer> depth = ThreadLocal.withInitial { 0 };
#Override
public void beforeTestClass(TestContext testContext) {
depth.set(depth.get() + 1);
if (depth.get() > 1)
return; // #Nested classes share the MockitoSession of the parent class
mockitoSession.set(
Mockito.mockitoSession()
.strictness(Strictness.STRICT_STUBS)
.startMocking()
);
}
#Override
public void afterTestClass(TestContext testContext) {
depth.set(depth.get() - 1);
if (depth.get() > 0)
return; // #Nested classes should let the parent class end the MockitoSession
MockitoSession session = mockitoSession.get();
if (session != null)
session.finishMocking();
mockitoSession.remove();
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
Then, MyMockitoTestExecutionListener can be added as a listener in test classes:
#SpringBootTest
#TestExecutionListeners(
listeners = {MyMockitoTestExecutionListener.class},
mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MySpringBootTests {
#MockBean
Foo mockFoo;
// Tests using mockFoo...
}
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.
I would like to perform a junit test using Mockito on the toEntity function.
#Component
public class MyEntityTransform {
public Function<MyDTO , MyEntity> toEntity = new Function<MyDTO , MyEntity >() {
#Override
public MyEntity apply(MyDTO record) {
return new MyEntity();
}
};
}
Unfortunately the toEntity is NULL when I mock the class and I don't know how I can test it correctly.
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#InjectMocks
private MyService _classUnderTest;
#Mock
private MyEntityTransform myEntityTransform
#Before
public void setUp() {
Mockito.when(this.myEntityTransform.toEntity.apply(Mockito.anyObject())).thenReturn(...);
}
}
When I RUN the JUNIT test, Mockito give me the error :
java.lang.NullPointerException
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
-> at com.example.MyTest.setUp(MyTest.java:38)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
Also, this error might show up because you use argument matchers with
methods that cannot be mocked. Following methods cannot be
stubbed/verified: final/private/equals()/hashCode(). Mocking methods
declared on non-public parent classes is not supported.
Do you have suggestions?
You're using public fields, which is not a good idea. But anyway, what you want to mock is the function, not the instance of MyEntityTransform. So you would need something like
#InjectMocks
private MyService _classUnderTest;
#Mock // or #Spy
private MyEntityTransform myEntityTransform;
#Before
public void prepare() {
myEntityTransform.toEntity = mock(Function.class);
}
But quite frankly, I wouldn't use a public field of type Function. Instead, I would use a public method:
public class MyEntityTransform {
public MyEntity toEntity(MyDTO record) {
return new MyEntity();
}
}
Then you can mock MyEntityTransform and make its toEntity method return what you want. And if you need to pass a Function doing what the method does, use a method reference:
collection.stream().map(myEntityTranform::toEntity)
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());
I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}