读程序员的制胜技笔记06_测试(下)

这篇具有很好参考价值的文章主要介绍了读程序员的制胜技笔记06_测试(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

读程序员的制胜技笔记06_测试(下)文章来源地址https://www.toymoban.com/news/detail-745989.html

1. 决定测试对象

1.1. 确保团队产出可靠的测试

1.1.1. 从成品代码中随机删掉几行,然后运行测试

1.1.2. 如果代码在这种情况下依然测试通过,就意味着程序员写的代码失败了

1.2. 规范是很好的出发点,但你不容易在行业内找到相关规范

1.3. 尊重边界

1.3.1. 为网络游戏检查用户是否达到法定年龄

1.3.1.1. 假设18岁是你游戏用户的法定年龄

1.3.2. 拦截算法

1.3.2.1. C#

public static bool IsLegalBirthdate(DateTime birthdate) {
  const int legalAge = 18;
  var now = DateTime.Now;
  int age = now.Year - birthdate.Year;
  if (age == legalAge) {  ⇽--- 代码里的条件判定
    return now.Month > birthdate.Month
      || (now.Month == birthdate.Month
          && now.Day > birthdate.Day);
  }
  return age > legalAge;
}  ⇽--- 
1.3.2.1.1. 不需要测试公元1年1月1日到9999年12月31日之间所有可能的DateTime值(有360多万个)
1.3.2.1.2. 只需要测试7个不同的输入
1.3.2.1.3. 通过条件语句将输入范围进行分割的操作称为“边界条件”(boundary conditional)
1.3.2.1.3.1. 定义了输入数据的边界

1.3.3. 数据绑定测试(data-bound test),参数化测试可以为你节省大量的时间

1.3.4. C#

[TestCase(18, 0, -1, ExpectedResult = true)]
[TestCase(18, 0, 0, ExpectedResult = false)]
[TestCase(18, 0, 1, ExpectedResult = false)]
[TestCase(18, -1, 0, ExpectedResult = true)]
[TestCase(18, 1, 0, ExpectedResult = false)]
[TestCase(19, 0, 0, ExpectedResult = true)]
[TestCase(17, 0, 0, ExpectedResult = false)]
public bool IsLegalBirthdate_ReturnsExpectedValues(
  int yearDifference, int monthDifference, int dayDifference) {
  var now = DateTime.Now;
  var input = now.AddYears(-yearDifference)  ⇽--- 在这准备我们的实际输入
    .AddMonths(monthDifference)
    .AddDays(dayDifference);  ⇽--- 
  return DateTimeExtensions.IsLegalBirthdate(input);
}

1.4. 代码覆盖率

1.4.1. 代码覆盖率是一种魔法,而且和其他魔法一样,具有被夸大的成分

1.4.2. 在开发环境中很少有开箱即用的代码覆盖率测量工具

1.4.2.1. 存在于Visual Studio的“天价版本”中

1.4.2.2. 存在于其他付费的第三方工具中

1.4.2.2.1. NCrunch、dotCover、and NCover

1.4.2.3. Codecov是一个可以与你的在线代码库对接的服务,并且它有免费计划可选

1.4.2.4. Visual Studio Code中的Coverlet库和代码覆盖率报告扩展可以在.NET本地进行免费的代码覆盖率测量

1.4.3. 代码覆盖率工具能为工作开一个好头,但它们在显示实际测试覆盖率方面并不完全有效

2. 不要写测试

2.1. 不要写代码

2.1.1. 如果一段代码不存在,它就不需要被测试。被删掉的代码是没有bug的。

2.1.2. 被删掉的代码是没有bug的

2.1.3. 你可能想写自定义的正则表达式来验证URL,而你所需要做的只是利用System.Uri类

2.1.4. 第三方代码并不能保证是完美的,也不一定适合你的目的

2.2. 不要一次写完所有的测试

2.2.1. 80%的后果是20%的原因所导致的

2.2.1.1. 帕累托法则(Pareto principle)

2.2.2. bug不会连续出现

2.2.3. 不是每一行代码都有相同的概率产生一个错误

2.2.4. 冒烟测试(smoke testing)

2.2.5. 对关键的、共享的组件有良好的测试覆盖率比100%的代码覆盖率更重要

3. 让编译器测试你的代码

3.1. 在强类型语言中,你可以利用类型系统来减少需要的测试数量

3.2. 正则表达式的“神话”

3.2.1. 正则表达式无疑是计算机科学史上最牛的发明之一

3.2.2. 斯蒂芬·科尔·克莱因(Stephen Cole Kleene)

3.2.3. 正则表达式不能感知上下文

3.2.3.1. 不能用一个正则表达式来寻找HTML文档中最内层的标签

3.2.3.2. 不能检测不匹配的关闭标签

3.2.4. 不适合用于复杂的解析任务

3.2.5. 可以用来解析具有非嵌套结构的文本

3.2.6. 在它适合的情况下还是有让人惊讶的性能的

3.2.7. 如果存在更简单的替代方法,你就不应该使用正则表达式

3.2.7.1. 常见的字符串检查操作,如StartsWith、EndsWith、IndexOf、LastIndexOf、IsNullOrEmpty以及IsNullOrWhiteSpace

3.2.7.2. 倾向于使用所提供的方法,而不是正则表达式

3.3. 消除null检查

3.3.1. 对Username类所在的项目启用可空引用,那我们就根本不用为null情况写测试

3.3.2. 例外是,当我们在写一个公共API的时候,它可能不会在可感知可空引用(nullable-reference-aware)的代码中运行

3.3.2.1. 还得进行null检查

3.3.3. 在恰当的时候将Username声明为结构(struct),它就会成为一个值类型,这也让它免去了进行null检查的操作

3.4. 消除范围检查

3.4.1. 使用无符号整数类型,可以减小可能的无效输入值的范围

3.4.1.1. 当你使用无符号类型时,你往函数引入一个负值就会引起编译错误

3.4.1.2. 如果要传一个负值,只能通过明确的类型转换才可以达成

3.4.1.3. 验证负值参数就再也不是函数的任务了

3.4.2. byte类型

3.4.2.1. 不是int类型

3.4.2.2. 标签的数量根本没有超过255个

3.4.2.2.1. 需要超过255个条目,你就必须用short或int来替换所有对byte的引用

3.4.2.3. 避免了输入值可能是负值或者太大的情况

3.4.2.4. 不用再对输入值进行验证了

3.4.2.4.1. 省掉了一次测试,而且输入值的范围也大为改善,显著减少了错误的发生

3.4.3. 你的舒适度和你的时间才是最重要的,而使用数据类型来让数据自带有效值范围带来的好处只是其次

3.5. 消除有效值检查

3.5.1. 单独的File.Create、File.OpenRead和File.OpenWrite方法了,这可以避免添加额外参数和对该参数的解析操作,就不可能传递错误的参数了

3.5.2. 在C#中,一个常见的技术是使用布尔参数来改变运行函数的逻辑

3.5.2.1. C#

public IList<Tag> GetTrendingTags(byte numberOfItems,
  bool sortByTitle, bool yesterdaysTags) {  ⇽--- 更多参数
  var query = yesterdaysTags  ⇽--- 更多条件句
    ? db.GetTrendingTagTable()
    : db.GetYesterdaysTrendingTagTable();
  if (sortByTitle) {  ⇽--- 
    query = query.OrderBy(p => p.Title);
  }
  return query.Take(numberOfItems).ToList();
}

3.5.3. 互相独立的函数

3.5.3.1. C#

public IList<Tag> GetTrendingTags(byte numberOfItems) {  ⇽--- 我们通过函数名而不是参数来分隔函数

  return db.GetTrendingTagTable()
    .Take(numberOfItems)
    .ToList();
}
public IList<Tag> GetTrendingTagsByTitle(  ⇽--- 
  byte numberOfItems) {
  return db.GetTrendingTagTable()
    .OrderBy(p => p.Title)
    .Take(numberOfItems)
    .ToList();
}
public IList<Tag> GetYesterdaysTrendingTags(byte numberOfItems) {  ⇽--- 
  return db.GetYesterdaysTrendingTagTable()
    .Take(numberOfItems)
    .ToList();
}

3.5.3.2. 省了一次测试

4. 命名测试

4.1. 一个名称可以包含很多信息

4.1.1. 被测试的函数的名称

4.1.2. 输入和输出状态

4.1.3. 预期的行为

4.2. 使用“A_B_C”的格式来命名测试

5. 总结

5.1. 先让自己能够不写测试,通过思考过程来克服之前对测试的偏见

5.2. 以测试驱动开发和类似的条框会让你更讨厌写测试

5.3. 写测试的工作时间可以通过测试框架大大缩短,特别是参数化、数据驱动的测试

5.4. 通过正确分析函数输入的边界值,可以大大减少测试的数量

5.5. 正确使用数据类型可以让你避免写许多不必要的测试

5.6. 测试不只可以确保代码的质量,也可以帮助你提高自己的开发技能和产出

到了这里,关于读程序员的制胜技笔记06_测试(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 读程序员的制胜技笔记13_安全审查(上)

    5.6.1.1. 任何你不想丢失或泄露的东西都是资产,包括你的源代码、设计文档、数据库、私钥、API令牌、服务器配置,还有Netflix观看清单 5.6.2.1. 每台服务器都会被一些人访问,而每台服务器都会访问其他一些服务器 6.1.1.1. 设计时首先要考虑到安全问题,因为在既有基础上去

    2024年02月05日
    浏览(61)
  • 读程序员的制胜技笔记14_安全审查(下)

    1.2.2.1. 看不出来是什么?那我拒绝为你服务 1.4.1.1. 工作量证明相当消耗客户端的运算资源,对那些性能较低的设备不友好,并且它还会影响设备电池的使用寿命 1.4.1.2. 有可能会严重降低用户体验,其后果甚至比验证码的还要恶劣 3.5.2.1. 存储需求更少,性能更强,数据管理

    2024年02月05日
    浏览(45)
  • 读程序员的制胜技笔记10_可口的扩展

    2.8.3.1. 纯函数有一个好处,它们是100%线程安全的 2.9.1.1. 这套数据结构并不都是无锁的 2.9.1.2. 虽然它们依然使用锁,但它们是被优化过的,锁的持续时间会很短,保证了其速度,而且它们可能比真正的无锁替代方案更简单 2.9.2.1. 其中原始数据从未改变,但每个修改操作都

    2024年02月05日
    浏览(67)
  • 读程序员的制胜技笔记09_死磕优化(下)

    7.5.3.1. 在256KB之后,提升突然变得杯水车薪

    2024年02月05日
    浏览(73)
  • 读程序员的制胜技笔记08_死磕优化(上)

    4.3.1.1. 只能给你一堆用于比较的数字 4.3.1.2. 不能告诉你代码的运行速度是快还是慢 4.3.1.3. 可以告诉你它们比其他一些代码运行得慢还是快 4.3.5.1. 可以消除因测量误差或者调用开销产生的波动 4.3.5.2. 适用于微观基准测试 4.3.5.2.1. 适用于微观基准测试 4.3.5.3. 基准测试并没

    2024年02月05日
    浏览(80)
  • 读程序员的制胜技笔记02_算法与数据结构

    3.1.1.1. 根据你的需要,可以有更智能的算法 3.1.3.1. 算法本身并不意味着它很聪明 3.2.1.1. public static bool Contains(int[] array, int lookFor) { for (int n = 0; n < array.Length; n++) {        if (array[n] == lookFor) {            return true;        }    }    return false; } 3.3.1.1. public sta

    2024年02月06日
    浏览(62)
  • 读程序员的制胜技笔记11_与Bug共存(上)

    2.7.3.1. 在构造时验证其有效性,这样一来就不可能包含无效值 2.8.2.1. 其主张一个花括号与声明在同一行 2.9.1.1. 看看这些现成的类型 2.9.3.1. 它代表持续时间 2.9.3.2. 你没有理由用TimeSpan以外的任何东西来表示持续时间,即使你所调用的函数不接受TimeSpan作为参数 2.9.4.1. 它也

    2024年02月05日
    浏览(55)
  • 读程序员的制胜技笔记12_与Bug共存(下)

    2.2.1.1. 故障代码(failing code)放在一个try语句块里,然后加上一个空的catch语句块,就大功告成了 2.2.1.2. 开发者为整个应用程序添加了一个通用的异常处理程序,但实际上这个程序的工作原理就是忽略所有的异常,也就防止所有的崩溃 2.2.1.3. 如果像那样添加一个空的处理程序

    2024年02月05日
    浏览(58)
  • 读程序员的制胜技笔记03_有用的反模式(上)

    4.5.4.1. 你在物理数据库结构上增加了一个依赖项 4.5.4.2. 如果你需要改变信息表的布局或所使用的数据库技术,你就必须检查所有的代码,确保所有的东西都能与新的表布局或新的数据库技术一起工作 4.5.6.1. 这是保持组件或整个层尽可能简单的关键 4.8.3.1. 每个成员只对自己

    2024年02月06日
    浏览(46)
  • 读程序员的制胜技笔记04_有用的反模式(下)

    1.3.1.1. 自己做自己的甲方 3.2.2.1. 紧耦合(tight coupling) 3.2.2.2. 依赖性是万恶之源 3.3.7.1. 因为你可能需要用接口而不是具体的引用来定义依赖关系,但这也会使代码摆脱依赖关系 5.2.3.1. 没有其他错误发生时执行的代码部分 5.3.3.1. 退出点(exit point)是指函数中导致其返回给调用

    2024年02月06日
    浏览(85)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包