如果要對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;
}
這樣就能開展正常的測試了。