A unit test should exhibit the following characteristics.
- It should be automated.
- It should have a fast test execution.
- A test should not depend on the result of another test or rather test execution order. Unit test framework can execution tests in any order.
- A test should not depend on database access, file storage, or any long running task. Rather, an appropriate test double should isolate the external dependencies.
- A test result should be consistent and time-and-location transparent.
- Tests should be meaningful.
- Tests should be short and tests should not be treated as second-class citizens.
1.Mock变量
1).类属性
public class UserDao {
public int getCount(){
throw new UnsupportedOperationException();
}
public void insertUser(User user) {
throw new UnsupportedOperationException();
}
}
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public int queryUserCount(){
return userDao.getCount();
}
public void saveUser(User user){
userDao.insertUser(user);
}
}
import heju.powermock.dao.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import static org.junit.Assert.*;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
public class UserServiceTest {
private UserDao userDao;
private UserService userService;
@Before
public void setUp() {
userDao = PowerMockito.mock(UserDao.class);
userService = new UserService(userDao);
}
@Test
public void queryUserCount() {
PowerMockito.doReturn(10).when(userDao).getCount();
int count = userService.queryUserCount();
assertEquals(10, count);
}
@Test
public void saveUser() {
User user = PowerMockito.mock(User.class);
PowerMockito.doNothing().when(userDao).insertUser(user);
userService.saveUser(user);
Mockito.verify(userDao).insertUser(user);
}
@After
public void destroy(){
reset(userDao);
reset(userService);
}
}
2).局部变量whenNew…then…
public class UserService {
public int queryUserCount(){
UserDao useDao = new UserDao();
return useDao.getCount();
}
public void saveUser(User user){
UserDao useDao = new UserDao();
useDao.insertUser(user);
}
}
import heju.powermock.dao.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService2.class)
public class UserService2Test {
private UserService2 userService;
private UserDao userDao;
@Before
public void setUp(){
userService = new UserService2();
userDao = mock(UserDao.class);
}
@Test
public void queryUserCount() {
try {
whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
doReturn(10).when(userDao).getCount();
int count = userService.queryUserCount();
assertEquals(10, count);
} catch (Exception e) {
fail();
}
}
@Test
public void saveUser() {
try {
User user = mock(User.class);
whenNew(UserDao.class).withAnyArguments().thenReturn(userDao);
// whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
doNothing().when(userDao).insertUser(user);
userService.saveUser(user);
verify(userDao).insertUser(user);
} catch (Exception e) {
fail();
}
}
@After
public void destroy(){
reset(userDao);
reset(user);
}
}
2.Mock静态方法
mockStatic
public class User {
}
import heju.powermock.service.User;
public class UserDao {
public static int getCount(){
throw new UnsupportedOperationException();
}
public static void insertUser(User user) {
throw new UnsupportedOperationException();
}
}
import heju.powermock.dao.UserDao;
public class UserService {
public int queryUserCount(){
return UserDao.getCount();
}
public void saveUser(User user){
UserDao.insertUser(user);
}
}
import heju.powermock.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class, UserDao.class})
public class UserServiceTest {
@Test
public void queryUserCount() {
mockStatic(UserDao.class);
UserService userService = new UserService();
when(UserDao.getCount()).thenReturn(10);
int count = userService.queryUserCount();
assertEquals(10, count);
}
@Test
public void saveUser() throws Exception {
User user = new User();
mockStatic(UserDao.class);
doNothing().when(UserDao.class);
UserService userService = new UserService();
userService.saveUser(user);
// 此处报错,暂未解决
verifyStatic(UserDao.class);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPu3VAZF-1642946215902)(/home/heju/.config/Typora/typora-user-images/image-20211228200042139.png)]
3.Mock final
final修饰的class和method
@PrepareForTest({finalClass.class})注解中加入final对应的class即可
public final class UserDao {
public int getCount(){
throw new UnsupportedOperationException();
}
public final void insertUser(User user) {
throw new UnsupportedOperationException();
}
}
import heju.powermock.dao.UserDao;
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public int queryUserCount(){
return userDao.getCount();
}
public void saveUser(User user){
userDao.insertUser(user);
}
}
import heju.powermock.dao.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class, UserDao.class})
public class UserServiceTest {
private UserDao userDao;
private UserService userService;
@Before
public void setUp() {
userDao = mock(UserDao.class);
userService = new UserService(userDao);
}
@Test
public void queryUserCount() {
when(userDao.getCount()).thenReturn(10);
int count = userService.queryUserCount();
assertEquals(10, count);
}
@Test
public void saveUser() {
User user = mock(User.class);
doNothing().when(userDao).insertUser(user);
userService.saveUser(user);
verify(userDao).insertUser(user);
}
@After
public void tearDown() {
reset(userDao);
}
}
4.spy与Mock私有方法
public class UserService {
public void foo(String str){
log();
}
private void log(){
System.out.println("log info");
}
public boolean check(String arg){
return exists(arg);
}
private boolean exists(String arg) {
throw new UnsupportedOperationException();
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import static org.junit.Assert.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
@Test
public void foo() throws Exception {
UserService userService = PowerMockito.spy(new UserService());
String arg = "hello";
PowerMockito.doNothing().when(userService).foo(arg);
// 不调用真实方法
userService.foo(arg);
// 调用真实方法
userService.foo("");
Whitebox.invokeMethod(userService, "log");
PowerMockito.verifyPrivate(userService).invoke("log");
}
@Test
public void check() throws Exception {
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.doReturn(true).when(userService, "exists", "alex");
assertTrue(userService.check("alex"));
try {
assertTrue(userService.check("connie"));
}catch (Exception e){
assertTrue(e instanceof UnsupportedOperationException);
}
}
}
5.verify的使用
import heju.powermock.service.User;
public class UserDao {
public int getCount(User user){
throw new UnsupportedOperationException();
}
public void insertUser(User user) {
throw new UnsupportedOperationException();
}
public void updateUser(User user) {
throw new UnsupportedOperationException();
}
}
import heju.powermock.dao.UserDao;
public class UserService {
public void saveUser(User user){
UserDao userDao = new UserDao();
if (userDao.getCount(user) > 0){
userDao.updateUser(user);
}else{
userDao.insertUser(user);
}
}
}
import heju.powermock.dao.UserDao;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Mockito.reset;
import static org.powermock.api.mockito.PowerMockito.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
private User user;
private UserDao userDao;
@Before
public void setUp() throws Exception {
user = mock(User.class);
userDao = mock(UserDao.class);
whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
}
@Test
public void saveUser_Insert() {
when(userDao.getCount(user)).thenReturn(0);
UserService userService = new UserService();
userService.saveUser(user);
Mockito.verify(userDao).insertUser(user);
Mockito.verify(userDao, Mockito.never()).updateUser(user);
}
@Test
public void saveUser_Update() {
when(userDao.getCount(user)).thenReturn(1);
UserService userService = new UserService();
userService.saveUser(user);
Mockito.verify(userDao, Mockito.never()).insertUser(user);
Mockito.verify(userDao).updateUser(user);
}
@After
public void tearDown(){
reset(user);
reset(userDao);
}
}
6.Mock不同的构造函数
1).whenNew…withArguments
2).whenNew…withNoArguments
3).whenNew…withAnyArguments
public class UserDao {
private String username;
private String password;
public UserDao(String username, String password) {
this.username = username;
this.password = password;
}
public void insert(){
throw new UnsupportedOperationException();
}
}
public class UserService {
public void save(String username, String password){
UserDao userDao = new UserDao(username, password);
userDao.insert();
}
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
@Test
public void save() throws Exception {
UserDao userDao = PowerMockito.mock(UserDao.class);
String username = "heju";
String password = "123";
PowerMockito.whenNew(UserDao.class).withArguments(username, password).thenReturn(userDao);
PowerMockito.doNothing().when(userDao).insert();
UserService userService = new UserService();
userService.save(username, password);
Mockito.verify(userDao).insert();
}
}
7.ArgumentMatcher接口的使用
when…then不同参数返回相同值时使用
public class UserDao {
public String queryByName(String name) {
throw new UnsupportedOperationException();
}
}
public class UserService {
public String query(String name){
UserDao userDao = new UserDao();
return userDao.queryByName(name);
}
}
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.reset;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
private UserDao userDao;
private UserService userService;
@Before
public void setUp() throws Exception {
userDao = PowerMockito.mock(UserDao.class);
whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
userService = new UserService();
}
@Test
public void queryWithStub() {
when(userDao.queryByName("jack")).thenReturn("vip");
String name = userService.query("jack");
assertEquals("vip", name);
when(userDao.queryByName("alex")).thenReturn("vip");
name = userService.query("alex");
assertEquals("vip", name);
when(userDao.queryByName("connie")).thenReturn("vip");
name = userService.query("connie");
assertEquals("vip", name);
}
@Test
public void queryWithMatcher(){
when(userDao.queryByName(argThat(new myArgumentMatcher()))).thenReturn("vip");
assertEquals("vip", userService.query("jack"));
assertEquals("vip", userService.query("alex"));
assertEquals("vip", userService.query("connie"));
}
private static class myArgumentMatcher implements ArgumentMatcher<String> {
@Override
public boolean matches(String s) {
switch (s){
case "jack":
case "alex":
case "connie":
return true;
default:
return false;
}
}
}
@After
public void tearDown(){
reset(userDao);
}
}
8.Answer接口的使用
when…then不同参数返回不同值
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.reset;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
private UserDao userDao;
private UserService userService;
@Before
public void setUp() throws Exception {
userDao = PowerMockito.mock(UserDao.class);
whenNew(UserDao.class).withNoArguments().thenReturn(userDao);
userService = new UserService();
}
@Test
public void queryWithAnswer(){
when(userDao.queryByName(anyString())).then(invocation -> {
String argument = (String) invocation.getArguments()[0];
switch (argument){
case "jack":
return "svip";
case "alex":
return "vip";
case "connie":
return "black";
default:
return "active user";
}
});
assertEquals("svip", userService.query("jack"));
assertEquals("vip", userService.query("alex"));
assertEquals("black", userService.query("connie"));
assertEquals("active user", userService.query("monica"));
}
@After
public void tearDown(){
reset(userDao);
}
}