51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 473|回复: 0
打印 上一主题 下一主题

[转贴] 淘系用户平台技术团队单元测试建设

[复制链接]
  • TA的每日心情
    无聊
    1 小时前
  • 签到天数: 936 天

    连续签到: 3 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-6-8 09:40:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    为什么需要单元测试
      纵观优秀的开源工程,完备的单元测试总是必须的条件。通过这些单元测试,我们可以充分了解代码中相关类和方法的作用和核心逻辑,熟悉各种场景的运行情况。同时也因为有了单元测试,开源作者在接受各种feature的代码提交时才有稳定安全的保障。其实单元测试的重要性所有开发同学应该都了然于胸,同样TDD(测试驱动开发)也不是一个新的概念,但是真当我们落地实践时,又总会找出各种各样的理由来劝服自己下次一定好好写单元测试,这一次先放过自己。这些理由无外乎,开发周期太紧了; 测试同学能保证功能正确性;写单元测试代码量比业务代码还大; 又不是不能跑。所以虽然我们总是在追逐工程师文化,却又时不时放纵在放弃工程师底蕴的路上。
      单元测试是工程交付前质量保障的第一环,也无疑是软件工程质量保障的重要基石,有效的单元测试能够提前发现90%以上的代码Bug问题,同时也能防止代码的腐化,在工程重构演进时起到至关重要的作用。
      怎么写单元测试
      好的单元测试的几个要点
      摘自阿里巴巴开发规约:
      单元测试必须遵守AIR原则,单元测试必须具备Automatic(自动化),Independent(独立性),Repeatable(可重复)性;
      单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试;
      单元测试要保证测试粒度足够小。单元测试测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别;
      单元测试要遵守BCDE原则,Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等;Correct,正确的输入,并得到预期的结果;Design,与设计文档相结合,来编写单元测试;Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得到预期的结果;
      核心业务、核心应用、核心模块的增量代码要确保单元测试通过。
      单元测试编码范式
      这里主要以Mockito单元测试框架为模版:
      1、Mock : 通过when().thenReturn/thenAnswer/thenThrow 或者doReturn().when()等mock方式将依赖类方法进行模拟,模拟服务依赖或者中间结果。
      2、DO : 调用被测试类方法,执行测试链路。
      3、Verify : 校验执行结果正确性,通过Assert校验数据结果准确,通过Verify校验链路执行准确,通过expected=Exception.class校验异常链路。
    1. public class Test {
    2.   // 0. 依赖类
    3.   @Mock
    4.   DependencyClass dependencyClass;
    5.   // 0. 待测试类
    6.   @InjectMocks
    7.   TestClass testClass;
    8.   @Before
    9.   public void setUp() {
    10.       MockitoAnnotations.initMocks(this);
    11.   }
    12.   @Test
    13.   public void testMethod() {
    14.       // 1. Mock, 依赖方法,构造中间层数据
    15.       when(dependencyClass.someMehod(any())).thenReturn(mockData());
    16.       // 2. Do, 调用被测试类
    17.       Result result = testClass.testMehod();
    18.       // 3. Verify, 校验结果数据
    19.       Assert.assertEquals("some expected result string", result.getModel());
    20.   }
    21.   }
    复制代码
    当然写单元测试用例虽然套路比较模版化,但是我们也要充分利用单元测试框架(Junit/Mockito/PowerMock/Spock),掌握其中的一些技巧,才能写出快准狠的单元测试用例,这也是研发同学必须要掌握的基本功。关于如何利用单元测试框架这里不再赘述(详细可以参考阿里[url=]技术[/url]Java编程技巧之单元测试用例编写流程》)。
      单元测试编码提效
      IDEA上有很多单元测试插件,能够半自动化生成单元测试类文件,这里重点推荐TestMe插件。TestMe插件可以智能分析被测试类的依赖类,结合Mockito+Junit等单元测试框架,生成Mock/InjectMocks依赖关系,自动生成单元测试类。

    假设业务代码如下:
    1. @Component
    2.   public class DefaultMemberManager implements MemberManager {
    3.       @Autowired
    4.       private MemberDAO memberDAO;
    5.       @Autowired
    6.       private CacheManager cacheManager;
    7.       @Override
    8.       public Date queryActivationTime(long userId) {
    9.           Date activationTime = cacheManager.getActivationTime(userId);
    10.           if (activationTime == null) {
    11.               MemberDO memberDO = memberDAO.queryByUserId(userId);
    12.               if (memberDO != null) {
    13.                   cacheManager.saveActivationTime(userId, memberDO.getActiveTime());
    14.                   activationTime = memberDO.getActiveTime();
    15.               }
    16.           }
    17.           return activationTime;
    18.       }
    19.   }
    复制代码
    则通过TestMe快捷键COMMOND+N, 可以极速自动生成如下的单元测试类:
    1. public class DefaultMemberManagerTest {
    2.       @Mock
    3.       MemberDAO memberDAO;
    4.       @Mock
    5.       CacheManager cacheManager;
    6.       @InjectMocks
    7.       DefaultMemberManager defaultMemberManager;
    8.       @Before
    9.       public void setUp() {
    10.           MockitoAnnotations.initMocks(this);
    11.       }
    12.       @Test
    13.       public void testQueryActivationTime() throws Exception {
    14.           when(memberDAO.queryByUserId(anyLong())).thenReturn(null);
    15.           when(cacheManager.getActivationTime(anyLong())).thenReturn(
    16.               new GregorianCalendar(2022, Calendar.MARCH, 5, 23, 2).getTime());
    17.           Date result = defaultMemberManager.queryActivationTime(0L);
    18.           Assert.assertEquals(new GregorianCalendar(2022, Calendar.MARCH, 5, 23, 2).getTime(), result);
    19.       }
    20.   }
    复制代码
    团队单元测试建设
      覆盖率概念
      覆盖率是类JaCoCo插件通过javaagent挂载的方式,在单元测试命令运行时执行代码覆盖率检测,计算单元测试执行过程中所覆盖的代码比例来生成覆盖率。常见的覆盖率指标,又可进一步细分为语句覆盖率,条件覆盖率,分支覆盖率,路径覆盖率等。这里我们当前更为关注语句覆盖率和分支覆盖率,尤其是增量代码的覆盖率,更能体现变更代码的单元测试覆盖情况。
      如何进行单元测试
      这里我们借助于阿里研发平台Aone的测试实验室功能,Aone实验室支持测试任务插件的编排组合,通过独立的测试资源执行测试任务。所以我们将代码拉取插件,单元测试插件和覆盖率计算插件进行编排配置,形成最终的执行流:拉取代码;执行单元测试命令;单元测试结果解析;计算覆盖率。最终完成整个工程的单元测试覆盖率计算。

     单元测试覆盖率结果示例如下:

    什么时候触发单元测试
      单元测试任务主要通过持续交付流水线pipeline来集成,当前几个主要触发策略如下:
      1. 代码提交时,保证单元测试执行及时性。
      2. 代码审核时,保证代码审核通过的代码分支符合单元测试标准。
      3. 发布流程中,保证最终集成发布的所有分支代码符合单元测试标准。
      单元测试覆盖率卡点
      用户平台技术团队单元测试规范如下:
      ·单元测试用例通过率为100%
      · 单元测试增量代码行覆盖率为85%
      · 代码规范扫描增量问题总数为0个
      单元测试覆盖率报表
      为了更好的衡量单元测试的覆盖率情况,我们采用报表的形式统计每个应用,每个团队的代码单元测试覆盖率。

    总结
      当前团队内各应用(除边缘应用外)的单元测试增量代码覆盖率在2022年已经全部达到85%标准,最新平均增量代码行覆盖率达到88%,整体全量代码覆盖率平均提高20%。诚然单元测试覆盖率的提高不是最终的目的,覆盖率高不能完全代表工程质量高,但是一个没有单元测试或者单元测试覆盖率低的工程,其代码质量和稳定性必然不高。同时团队内研发同学对单元测试也有了新的认识,自测和提测质量显著提升,全年未发生由于代码质量产生的线上故障,有效提升了工程质量和服务稳定性。
      后续规划,持续优化单元测试质量,提升分支覆盖率,优化边界异常覆盖;关注单元测试编码效率的提升,优化测试用例和测试数据分离;关注核心链路单元测试覆盖率;熟练将TDD思维运用到业务开发过程中。





    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-4-24 11:10 , Processed in 0.068438 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表