I don't know why the test fails. feat JUnit - java

UserMapper.java
#Mapper
public interface UserMapper {
public List<UserResponseDto> selectUserList();
public void insertUser(UserSaveRequestDto userSaveRequestDto);
}
UserServiceImpl.java
#RequiredArgsConstructor
#Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
#Transactional
#Override
public Long insertUser(UserSaveRequestDto userSaveRequestDto) {
userMapper.insertUser(userSaveRequestDto);
return userSaveRequestDto.getUserId();
}
}
UserServiceTest.java
#ExtendWith(SpringExtension.class)
public class UserServiceTest {
#Mock
UserMapper userMapper;
#InjectMocks
UserServiceImpl userService;
// Mockito 이용한 테스트 코드
#DisplayName("Mock을 사용한 insertUser 테스트")
#Test
public void insertUser() {
// given
UserSaveRequestDto userSaveRequestDto = UserSaveRequestDto.builder()
.userName("test")
.userPhoneNumber("01026137832")
.build();
willDoNothing().given(userMapper).insertUser(userSaveRequestDto);
given(userService.insertUser(userSaveRequestDto)).willReturn(1L);
// when
Long userId = userService.insertUser(userSaveRequestDto);
// then
//mockito 스타일
verify(userMapper).insertUser(userSaveRequestDto);
//BDDMockito 스타일
then(userMapper).should().insertUser(userSaveRequestDto);
assertThat(userId).isEqualTo(1L);
}
}
Exception
org.mockito.exceptions.misusing.CannotStubVoidMethodWithReturnValue:
'insertUser' is a void method and it cannot be stubbed with a
return value! Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod(); If you need to set the void method to do nothing you can use:
doNothing().when(mock).someVoidMethod(); For more information, check out the javadocs for Mockito.doNothing().
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
The method you are trying to stub is overloaded. Make sure you are calling the right overloaded version.
Somewhere in your test you are stubbing final methods. Sorry, Mockito does not verify/stub final methods.
A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
Mocking methods declared on non-public parent classes is not supported.
at com.example.mybatis.user.UserServiceTest.insertUser(UserServiceTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
I don't know why the test fails, please help me

Related

#Transactional annotation overwrites Mockito's InjectMocks

In class TeacherDao method create() calls AddressDao.create():
public void create(Teacher teacher) {
addressDao.create(teacher.getAddress());
// Teacher is being written to database...
}
I am writing a Junit5 test for TeacherDao class, so AddressDao is replaced with a mock.
I call TeacherDao.create() and verify that AddressDao.create() is being interacted:
#Mock
private AddressDao addressDao;
#InjectMocks
#Autowired
private TeacherDao teacherDao;
#Test
void teacherDaoCreateTest() {
teacherDao.create(teacher);
verify(addressDao).create(testAddress);
}
Verification is successful.
After that I add #Transactional annotation to TeacherDao.create() method, and test fails.
AddressDao mock now has zero interactions, and testAddress is being actually written to the database.
I don't totally understand how #Transactional works in-depth and must be missing something important. Why #Transactional rewrites mocks?
I think you should delete the #Autowired annotation.

Using verify on Spy object

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

Injected Mock behavior not used in constructor

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.

How to JUNIT a java.util.Function using Mockito

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)

Doubts with mockito

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

Categories