进阶使用
1. 参数化批量测试
在测试数据比较多的时候,我们可以通过 @DataProvider 生成数据源,通过 @Test(dataProvider = "xxx") 使用数据, 如下所示:
import com.test.testng.BaseTest; import com.test.testng.dto.UserDto; import org.mockito.InjectMocks; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.assertFalse; import static org.testng.AssertJUnit.assertTrue; public class UserServiceTest2 extends BaseTest { @InjectMocks private UserService userService; // 定义数据源 @DataProvider(name = "test") public static Object[][] userList() { UserDto dto1 = new UserDto(); UserDto dto2 = new UserDto(); dto2.setSex(1); UserDto dto3 = new UserDto(); dto3.setSex(1); dto3.setFlag(1); UserDto dto4 = new UserDto(); dto4.setSex(1); dto4.setFlag(1); dto4.setAge(1); return new Object[][] {{dto1, null}, {dto2, null}, {dto3, null}, {dto4, null}}; } // 正确场景 @Test public void testCheckEffectiveUser() { UserDto dto = new UserDto(); dto.setSex(1); dto.setFlag(1); dto.setAge(18); boolean result = userService.checkEffectiveUser(dto); assertTrue(result); } // 错误场景 @Test(dataProvider = "test") public void testCheckEffectiveUser(UserDto dto, Object object) { boolean result = userService.checkEffectiveUser(dto); assertFalse(result); } }
2. 复杂判断保证测试覆盖率
案例:
判断有效用户: 年龄大于 18 并且 sex = 1 并且 flag = 1
public boolean checkEffectiveUser(UserDto dto) { // 判断有效用户: 年龄大于 18 并且 sex = 1 并且 flag = 1 return Objects.equals(dto.getSex(), 1) && Objects.equals(dto.getFlag(), 1) && dto.getAge() != null && dto.getAge() >= 18; }
拆分逻辑。将其转换为最简单的 if ... else 语句。然后增加的单元测试,如下所示:
public boolean checkEffectiveUser(UserDto dto) { if (!Objects.equals(dto.getSex(), 1)) { return false; } if (!Objects.equals(dto.getFlag(), 1)) { return false; } if (dto.getAge() == null) { return false; } if (dto.getAge() < 18) { return false; } return true; }
拆分后我们可以看到,咱们只需要 5 条单元测试就能做到全覆盖。
public class UserServiceTest extends BaseTest { @InjectMocks private UserService userService; // 覆盖第一个 return @Test public void testCheckEffectiveUser_0() { UserDto dto =new UserDto(); boolean result = userService.checkEffectiveUser(dto); assertFalse(result); } // 覆盖第二个 return @Test public void testCheckEffectiveUser_1() { UserDto dto =new UserDto(); dto.setSex(1); boolean result = userService.checkEffectiveUser(dto); assertFalse(result); } // 覆盖第三个 return @Test public void testCheckEffectiveUser_2() { UserDto dto =new UserDto(); dto.setSex(1); dto.setFlag(1); boolean result = userService.checkEffectiveUser(dto); assertFalse(result); } // 覆盖第四个 return @Test public void testCheckEffectiveUser_3() { UserDto dto =new UserDto(); dto.setSex(1); dto.setFlag(1); dto.setAge(1); boolean result = userService.checkEffectiveUser(dto); assertFalse(result); } // 覆盖第五个 return @Test public void testCheckEffectiveUser_4() { UserDto dto =new UserDto(); dto.setSex(1); dto.setFlag(1); dto.setAge(18); boolean result = userService.checkEffectiveUser(dto); assertTrue(result); } }
单测覆盖率检测检测
3. 通过断言校验方法参数
assert:断言是 java 的一个保留字,用来对程序进行调试,后接逻辑运算表达式,如下:
int a = 0, b = 1; assert a == 0 && b == 0; // 使用方法:javac编译源文件,再java -ea class文件名即可。
在 Spring-Boot 中可以使用 Spring 提供的 Assert 类的方法对前端来的参数进行校验,如:
// 检查年龄 >= 18 岁 public boolean checkUserAge(UserDto dto){ Assert.notNull(dto.getAge(), "用户年龄不能为空"); Assert.isTrue(dto.getAge() >= 18, "用户年龄不能小于 18 岁"); return Boolean.TRUE; }
如果是需要转换为,rest api 返回的统一相应消息,我们可以通过:
@ControllerAdvice public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = IllegalArgumentException.class) public Response<String> handleArgError(IllegalArgumentException e){ return new Response().failure().message(e.getMessage()); } }
如何设计程序
在功能模块的设计过程中我们因该遵循一下原则(参考 《软件工程-结构化设计准则》):
- 模块大小适中
- 合适的系统调用深度
- 多扇入、少扇出(增加复用度, 减少依赖程度)
- 单入口,单出口
- 模块的作用域,应该在模块内
- 功能应该可以预测的
- 高内聚,低耦合
- 系统分解有层次
- 较少的数据冗余