基本原则
- 测试任何可能失败的事物。测试主执行路径很好,而且很需要做;但测试异常处理可能更重要。
- 测试先行。在写任何代码之前,必须先写一个失败的测试。
- 为还没有实现的测试代码抛出一个异常。这就避免了该测试通过,而且会提醒你必须实现其代码。
- 不应该依赖于任何不可控的外部服务,如xingng,使用mock替代
- 不应该依赖于外部的数据(即不要依赖数据库中现有数据等,以保证数据库变更等不会影响到测试结果),可以在单元测试中自己准备数据
- 测试用基础数据准备
- 手工制造(简单数据,手工设值或者使用MockTestUtil提供的方法)
- 文件载入(文本-xml,json;二进制-java对象序列化文件等)
- 测试动作不应影响持久化数据,即不应该对数据库等造成永久性修改,数据库类操作尽量回滚,对于现有测试用例中的 rollback=false 尽量去除
- 单元测试需要测试到server层,junit原则上只应用service。
- 测试优先级
- 基本的数据库操作方法,测试优先级从高到低依次为 service > manager > dao
- helper,util等工具类需要单独测试,不能依赖调用方间接的测试
编码规范
- CRUD测试,基本的方式:建议CRUD每个动作对应一个测试方法,以便隔离各个操作相互间的影响
- 测试方法名应遵守test_XX1_XX2的命名模式,其中XX1是待测方法名,XX2是测试条件或目的,应当能通过阅读方法名就可以理解要测试的是什么方法,如test_loginByLdap_account_not_exists,test_loginByLdap_pass
- 一个测试方法应只有一个目的,如测试登录失败和登录成功应分为两个测试
- 在assert调用中解释失败原因。尽量使用第一个参数是String类型的那个方法,这个参数让你可以提供一个有意义的文本描述,用于在断言失败的时候显示。同样的,fail()调用也应提供文本描述。
- 选择合适的assert方法使用,而不仅仅是assertTrue
- 注意assert方法的参数顺序,期望值在前,实际值在后
- 代码样例
|
|
FAQ
- 预期抛出异常使用 @Test(except=xxException.class) 方式
- DAO CRUD 测试组织问题
- 一条数据字段被多次修改导致测试失败
- 外键约束的处理
- 使用现有数据
- 准备所有相关约束数据
- 测试环境、数据的统一准备
- 可以使用@Before修饰的方法,在每个测试方法调用前会自动执行
- 唯一约束冲突问题
- 使用 MockTestUtil 的 getJavaBean 方法生成的对象属性值可能会与数据库中现有记录相同,导致写入数据库失败,解决办法是 mock 出对象后针对该属性进行手工设值
- 测试用例编写粒度
- 基本的 DAO 级别 CRUD 测试可以少写一点
- 测试方法间的依赖性问题
- 测试方法间不应有先后顺序依赖
- 复杂方法的测试(逻辑较复杂、边界条件较多等)
- 可以分成多个测试用例,每个测试用例测试一个逻辑分支或编辑条件
- 数据写入MQ和数据库的ID不一致问题
- 数据库字段是char(2),java 中是String 执行testCRUD方法时,报错
- 解决方法:修改mockBeanFactory类中对应的方法,调整特殊字段的属性
- 无意义对象创建存在问题时,比如有外键约束,数据有效性判断等
- 可以 VehicleSiteManageVo vo = MockTestUtil. getJavaBean(VehicleSiteManageVo. class ); 构建对象,再针对特定数据构造
- 考虑数据查询量的大小,以及单元测试的运行时间
- 分区表写入问题
- 有时候使用 MockTestUtil 的 getJavaBean 方法生成的 PO 对象写入数据库时出现 DAOAccessException,错误编号为ORA-14400,这种错误是可能是因为对应表依据某个或某几个字段值做了表分区,mock 出来的对象该属性值超出了表分区所允许的值范围,从而出现这个错误。解决办法是找到对应表并查看建表 sql,按照该表的表分区定义将相应 PO 属性设置成表分区所允许的值。
关于测试基类
- AbstractServiceTest
- 一般的测试类请继承这个基类,不带xingng等外部依赖,如果业务实现中增加了新的xingng接口调用,需要到com.best.oasis.express.test.stub这个包下实现一个相应的stub类来模拟
- AbstractIntegrationServiceTest
- 带完整的外部依赖,需要真实调用xingng等外部系统的测试类才会继承这个基类,请尽量避免