如果要对service层进行mock测试,首先需要解决的就是autowired的问题,因为在使用的时候,框架会帮我们解决对象创建的问题,所以我们一般不会预留构造函数,这就给我们mock带来了一点麻烦。还好,mockito提供了解决方案,就是利用@InjectMocks
注解:
@InjectMocks
- Instantiates testing object instance and tries to inject fields annotated with@Mock
or@Spy
into private fields of testing object
这个注解会把所有用@Mock
和@Spy
注解的属性注入到被标记的属性里。比如:
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest extends AbstractJUnit4SpringContextTests {
@Mock
private UserRegistry userDao;
@InjectMocks
private UserService userService;
}
@Service
public class UserService {
@Autowired
private UserRegistry userDao;
}
这里就会把UserRegistry和LoginLogRegistry注入到UserService里。从名字里也可以看出,这两个是DAO层的内容,是在service里被autowired的。如果这里报错的话,之前看到有人说,可以加一个这个,用来初始化mock(虽然我这里不用):
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
mock完之后就是测试业务逻辑了。假如service里有这样的一个逻辑,判断是否存在这样的一个用户:
@Service
public class UserService {
@Autowired
private UserRegistry userDao;
public boolean hasMatchUser(String userName, String password) {
long matchCount = userDao.countByUserNameAndPassword(userName, password);
return matchCount > 0;
}
}
public interface UserRegistry extends JpaRepository<User, Integer> {
long countByUserNameAndPassword(String userName, String password);
}
我们的测试代码可能会这么写:
when(userService.hasMatchUser("admin", "123456")).thenReturn(true);
但是如果这么写,会报错:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by countByUserNameAndPassword()
countByUserNameAndPassword() should return long
“正确”写法应该是这样:
doReturn(true).when(userService).hasMatchUser("admin", "123456");
但是这样仍然会报错:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!
因为service本身不是一个mock过的对象,所以应该把它“变”成一个mockito的对象,但是我们想调用的是真实的service,所以应该用spy而不是mock:
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest extends AbstractJUnit4SpringContextTests {
@Mock
private UserRegistry userDao;
@Spy
@InjectMocks
private UserService userService;
}
这样就能开展正常的测试了。