1. 单元测试

1.1. 为什么要做单元测试

  1. 帮助理解需求

    单元测试应该反映使用场景,把被测单元当成黑盒测试其外部行为。

  2. 提高实现质量

    单元测试不保证程序做正确的事,但能帮助保证程序正确地做事,从而提高实现质量。

  3. 测试成本低

    相比集成测试、验收测试,单元测试所依赖的外部环境少,自动化程度高,时间短,节约了测试成本。

  4. 反馈速度快

    单元测试提供快速反馈,把bug消灭在开发阶段,减少问题流到集成测试、验收测试和用户,降低了软件质量控制的成本。

  5. 利于重构

    由于有单元测试作为回归测试用例,有助于预防在重构过程中引入bug。

  6. 文档作用

    单元测试提供了被测单元的使用场景,起到了使用文档的作用。

  7. 对设计的反馈

    一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。

1.2. 解耦单元测试说明

覆盖率工具使用EclEmma,本地使用其eclipse插件

覆盖的类指的是接口的具体实现类或者具体实现类的子类

本地单元测试调试未通过禁止上传代码

1.2.1. 1、验收标准

  • 所有apps-api中对外接口实现单元测试100%
  • 所有接口javadoc中描述的场景单元测试实现100%
  • 方法覆盖100%,正常的场景100%覆盖,异常场景如觉得可以不做的要走评审
  • 单元测试通过率100%

1.2.2. 2、工程组织形式

  • 不允许工程间互相调用,只允许调用提供的接口进行数据构造

  • 在一个CI上构建:各组功能需要单元测试通过才打包;所有组构建通过才能聚合

  • 继承现有的TestCase的基类

  • 单元测试配置文件需指向apps-test

    编写单元测试时需要同步重构代码
    
    多个参数的方法拆分为多个子方法的组合,对子方法及组合过后的方法分别单元测试。
    如被测方法完全无法将单元测试方法控制在3个以内,请拆分接口或将实现类中的被测方法需要拆分为多个子方法的集合再对子方法进行单元测试。
    

1.2.3. 3、接口申请

  1. 开发组整理需要对外公布的接口,组长审核通过后发起接口申请协同
  2. 在申请协同中,需详细注明申请接口的内容,适用场景等信息
  3. 平台相关人员在收到接口申请后组织接口评审会议,需参加人员:接口申请人、该组开发组长、平台解耦委员会成员、相应的功能测试人员
  4. 评审会议中,平台解耦委员会成员判断该接口是否合理、是否适合对外开放;功能测试人员补充该接口的应用场景,作为单元测试实现依据
  5. 评审会议通过后,平台相关人员处理协同。开发组长在apps-api中增加该接口,接口申请人编写相应的实现代码及单元测试代码,确定通过后提交代码

1.2.4. 4、平台解耦委员会

平台解耦委员会需要处理单元测试中所遇到的疑难问题 包括但不限于:

  • 需要测试的方法因历史原因导致会花很多时间重构,并且有造成不稳定的风险
  • 方法、类可测但是设计难以达到验收标准
  • 新增/修改接口评审
  • 等等

1.3. 解耦单元测试规范

1.3.1. 命名规范

各组将单元测试代码放于工程中test目录下,包路径需与被测类路径一致。 本次解耦单元测试虽然规定的目标对象是接口,但是实际上是针对该接口的实现类进行单元测试。 实现类类名为接口类名+Impl。 测试类名为被测的实现类类名+Test,单元测试方法名为test+实现类方法名。 如有准备数据的类,类名为实现类名+DataInit 例子:

如接口类NewsApi
其实现类为NewsApiImpl
测试类为NewsApiImplTest
为测试类准备数据的类为NewsApiImplDataInit
其中的一个单元方法getNewsById的测试方法名为testGetNewsById
  • 平台会集成单元测试相关资源,各组调用其中的测试基类继承并编写、维护相应的单元测试代码。
  • 产品代码构建时,会在编译代码后执行单元测试代码,测试通过后才进行打包操作,如测试不通过不会进行打包。
  • 单元测试用例需要经过评审,必须参与的评审人员:开发经理、用例编写人员、单元测试实现人员

1.3.2. 接口JavaDoc编写规范

所有的接口设计完成后需评审公共再编写单元测试

  • apps-api的对外接口都需要有完善的JavaDoc
  • JavaDoc中需要有详细的接口内容描述、参数描述、返回值描述,javadoc中对其他类或者属性的引用必须加@link,让引用能直接链接查看
  • JavaDoc中需要写明接口的具体使用场景以及完整覆盖所需的数据情况

接口JavaDoc

/**
 * 获取某个单位或某个自定义空间公告板块列表
 * <p/>
 * <b>场景:</b><br>
 * 正常:<br>
 * 1.获取某个单位公告板块列表,能正常返回板块列表<br>
 * 2.获取某个自定义集团空间公告板块列表,能正常返回板块列表<br>
 * 3.获取某个自定义单位空间公告板块列表,能正常返回板块列表<br>
 * <p/>
 * 异常:<br>
 * 4.传入的id错误,返回空列表<br>
 *
 * @param accountId 单位id或空间id
 * @return 公告板块列表,如没有板块,返回空集合
 * @throws BusinessException BusinessException异常
 */
public List<BulType> findAccountBulletinTypes(Long accountId) throws BusinessException;

示例接口的单元测试的JavaDoc

/**
 * 1.获取某个单位公告板块列表,能正常返回板块列表:单位板块+3个预置板块(财务、人事、行政)
 * @throws BusinessException
 */
@Test
public void testFindAccountBulletinTypes_1() throws BusinessException {
    List<BulType> bulTypes = bulletinApi.findAccountBulletinTypes(BulletinApiDataInit.account.getId());
    AssertUtils.assertEquals(bulTypes.size(), 4);
    AssertUtils.assertCollectionContainsObject(bulTypes, BulletinApiDataInit.account_bulType);
}

/**
 * 2.获取某个自定义集团空间公告板块列表,能正常返回板块列表:自定义集团板块
 * @throws BusinessException
 */
@Test
public void testFindAccountBulletinTypes_2() throws BusinessException {
    List<BulType> bulTypes = bulletinApi.findAccountBulletinTypes(BulletinApiDataInit.public_custom_group_spaceId);
    List<BulType> expectList = new ArrayList<BulType>();
    expectList.add(BulletinApiDataInit.public_custom_group_bulType);
    AssertUtils.assertCollectionEquals(bulTypes, expectList);
}

/**
 * 3.获取某个自定义单位空间公告板块列表,能正常返回板块列表:自定义单位板块
 * @throws BusinessException
 */
@Test
public void testFindAccountBulletinTypes_3() throws BusinessException {
    List<BulType> bulTypes = bulletinApi.findAccountBulletinTypes(BulletinApiDataInit.public_custom_spaceId);
    List<BulType> expectList = new ArrayList<BulType>();
    expectList.add(BulletinApiDataInit.public_custom_bulType);
    AssertUtils.assertCollectionEquals(bulTypes, expectList);
}

/**
 * 4.传入的单位id错误,返回空列表
 * @throws BusinessException
 */
@Test
public void testFindAccountBulletinTypes_4() throws BusinessException {
    List<BulType> bulTypes = bulletinApi.findAccountBulletinTypes(40000L);
    AssertUtils.assertCollectionIsEmpty(bulTypes);
}

1.3.3. 测试方法编写规范

  • 测试方法名符合命名规范
  • 测试方法名后跟接口JavaDoc中的场景编号,一个测试方法对应一个场景
  • 一个测试方法必须有至少一个验证点(平台会提供断言方法),验证点方法必须有说明信息参数

1.4. 单元测试编写流程

  1. 相关负责人根据解耦目标和规范约定要编写的接口
  2. 开发人员编写接口,并根据规范编写在JavaDoc中编写好内容描述、参数描述、返回值描述
  3. 功能测试人员配合开发人员在接口JavaDoc中编写该接口可能存在的应用场景
  4. 开发人员根据接口的JavaDoc内容进行具体的功能实现,需满足所有的应用场景需求
  5. 测试人员配合开发人员进行接口中JavaDoc内的场景进行编写单元测试代码,每个场景对应一个单元测试

1.4.1. 可不强制要求实现的场景

  1. 取得对象时不强制要求验证null的情况(底层有统一的方法)
  2. 抛出异常BusinessException情况不用进行验证(接口统一规定),也不用JavaDoc

results matching ""

    No results matching ""