| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 开发测试 -> 单元测试最佳实践|如何避免常见陷阱? -> 正文阅读 |
|
[开发测试]单元测试最佳实践|如何避免常见陷阱? |
单元测试的目的是为了随着时间的变化,系统能够按预期工作。一来系统质量得到了保证,开发人员能够提前发现和解决问题,不用身陷bug的泥潭无法自拔;二来开发人员有更多的时间和精力去完善自己技术、提升自己的生活质量,从而形成一个良性循环。 我写了很多测试,也读了很多。他们中的大多数帮助我及早发现错误,提供代码文档并帮助回归测试。但我也发现一些单元测试没有做到这一点。相反,它们要么非常复杂,以至于无法弄清楚它们在测试什么,要么会随机失败,要么根本不会失败。 本文介绍了导致单元测试无效的五个陷阱,以及如何修复它们。 为每个函数编写一个单元测试看起来很简单。假设您有一个小函数可以做一件事。假设它被称为calculate_average。它是一个小单元,它是单元测试最佳实践希望您测试的单元。所以你为它写了一个测试,test_calculate_average. 这有什么问题?它测试单个代码单元,但它应该测试该单元的单个行为。通常这也被表述为在测试中只有一个断言。一个更好的测试将是test_calculate_average_return_0_for_empty_list. 一旦您拥有了其中的几个,他们就会免费为您提供详细的文档。 它还改变了您对如何编写测试的思维方式。您必须考虑您期望从函数中获得的不同行为。在不知不觉中,场景越来越多,因为您正在考虑边缘情况,甚至为它们编写测试,所以编写单元测试的收益也逐渐降低。
测试的重点应该是外部行为,如果我们过渡关注内部行为,当我们对实现逻辑进行了修改,那么原本的单元测试也就无法使用了,也起不到对代码重构保驾护航的作用了,违背了我们写单元测试的初衷,当然如果有一块内部逻辑,非常复杂,你也可以自己进行全覆盖测试,但一般情况下没有必要为了测试而测试。 只为代码覆盖率而编写测试跟踪测试覆盖率通常是一个好主意。如今,许多测试框架都支持这一点,并且像codecov这样的平台可以很容易地随着时间的推移对其进行跟踪。那么,为什么沉迷于它不是一个好的想法呢? 代码覆盖率只是一种测量工具。100% 的代码覆盖率并不意味着你已经覆盖了所有的边缘情况,它只是意味着所有的代码路径都被执行了。这是一个覆盖率 100% 的快速反例,但让我们探讨当您传入一个空列表时会发生什么?
代码覆盖率的根本问题是它只衡量覆盖了多少行程序。但所有程序都是状态机;要获得完整覆盖,您必须覆盖所有状态,但这是不可行的。 追求完整的,或者至少是非常高的覆盖率也会导致大量的测试,但并不是所有的测试都那么有用。对于 我没有努力覆盖每一行代码,而是推荐 Martin Fowler 的建议。将测试重点放在有风险的代码上。那是您自己编写的代码,而不是可能会被重构的框架。然而,知道什么是有风险的很困难,因为它需要经验。
特别是某个代码逻辑导致的线上bug,或者其它同学发现的问题,都可以编写成测试用例,防止此类错误的再次出现。 严重依赖Mock使用打桩模拟和存根对于单元测试是必不可少的。大多数情况下,您的被测代码与其他模块交互,并且在测试期间,您希望控制它们的行为。这可能导致你过度打桩。 当您必须编写 50 或 100 行模拟来测试单个函数时,那么您在测试什么?您是在测试您的函数,还是在测试您为测试该函数而编写的模拟? 许多Mock模拟也是危险信号。当您需要多个非常复杂的模拟来测试单个函数时,这个函数很可能复杂度过高。因此,您可能希望将其重构为几个功能较少且可以单独测试的函数。 我见过一些非常复杂的模拟。这是一个例子的再现:
这个时候,你不要想办法进行Mock模拟,而是考虑如何进行重构?让其变得更简单,更容易测试。 我们通常通过单元测试去保证代码质量,那么单元测试代码本身的质量又如何保证呢?所以我们的单元测试要写的尽可能简单。 对于对数据一致性要求不高的系统,甚至可以直接对着接口进行测试,这样省去了编写Mock的复杂度。 编写永不失败的单元测试正常情况下,回归是进行单元测试的原因之一。您编写代码,编写通过的测试并获得收益。万一有人破坏了您代码的功能,单元测试将能够发现问题。然而,另外一种情况,您的测试可能永远不会失败并且您会错过回归。 但是,您如何以永不失败的测试结束呢?下面是一个例子:
现在问问自己:哪些更改会导致此测试失败?最明显的一个是改变Mock模拟响应。但这不算数,您没有更改被测代码。更糟糕的是,我忘记了传递json.dumps参数. 这个错误不会被测试发现。另外有的同学为了保证测试覆盖率,甚至不写断言,直接打印输出,这样的话,可能永远不会出错。 这种问题被称为误报,看似无懈可击的测试用例,其实没什么用处,为了防止这种情况,请考虑是什么导致您的测试失败。更好的是,从失败的测试开始,然后编写代码直到它通过。在不知不觉中,您正在进行测试驱动开发。 使用单元测试保证非确定性行为的正确性这是一个众所周知的谬论。如果您的测试或被测代码以不确定的方式运行,您将对测试失去信心。每次失败时,你都会问:我的测试失败了,还是会通过重新运行?重新修改运行都会给你的测试用例带来修改的麻烦,你甚至想要放弃单元测试用例。 对于测试来说,不确定性的缺点是显而易见的,那么是什么导致了这种情况呢? 您是否在测试中使用当前时间或日期?如果是,则您的测试每天都在使用不同的数据运行。一旦您从事该行业的时间足够长,您就会遇到这些类型的测试。它们可能仅在该月的最后一天失败,或者仅在午夜之前开始并在之后完成。幸运的是,有一个简单的解决方案:控制时间的流动。例如,Python 具有用于此的freeze-gun模块。 您是否使用随机性来生成示例数据?有一个名为faker的 Python 库,它可以轻松生成真实的数据,如姓名、地址或电话号码。它非常适合填充演示环境或冒烟测试。对于单元测试不是那么有用,通常而言,使用硬编码的单元测试用例最可靠。 如果系统中存在不确定性,那么应该保证固定的逻辑不会出错,对于不确定性的边缘情况应该通过其它方式保证,比如开发、测试人员、寻找更稳定的类库等。 总结这就是阻止您编写有效单元测试的五个陷阱。既然您了解它们,您可以通过执行以下操作来避免它们:
这将使您的系统更加稳定,另外经过良好测试的软件让您可以自信地进行更改和快速部署。 引用https://github.com/google/googletest https://betterprogramming.pub/advanced-unit-tests-5-pitfalls-and-how-to-avoid-them-eb6e04ec9654 https://developer.ibm.com/articles/au-googletestingframework/ https://www.froglogic.com/blog/code-coverage-of-unit-tests-written-with-google-test/ 推荐原创不易,随手关注或者”在看“,诚挚感谢! |
|
开发测试 最新文章 |
pytest系列——allure之生成测试报告(Wind |
某大厂软件测试岗一面笔试题+二面问答题面试 |
iperf 学习笔记 |
关于Python中使用selenium八大定位方法 |
【软件测试】为什么提升不了?8年测试总结再 |
软件测试复习 |
PHP笔记-Smarty模板引擎的使用 |
C++Test使用入门 |
【Java】单元测试 |
Net core 3.x 获取客户端地址 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/18 2:46:47- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |