Stack Overflow 如何提升其单元测试水平

news2024/9/26 23:02:08

在 Stack Overflow 成立之初,我们只是一个快速、精简运行的网站。Stackoverflow.com 是由开发人员为开发人员创建的小型初创公司。像所有初创公司一样,我们优先考虑对我们来说最重要的质量属性,而忽视了许多其他属性,包括根据最佳实践进行单元测试。网站是为开发人员而建的,我们发现很多用户都很乐意报告错误,并在我们修复错误的同时解决它们。

几年前,我们推出了Stack Overflow for Teams Enterprise,突然拥有了一个大公司都在使用的付费产品。与社区网站用户不同,他们不想在生产中发现错误。我们有集成测试套件,但测试基础架构,尤其是单元测试,远远落后于产品的成熟度。

我们正在努力改变这种状况。端到端测试和集成测试很好,也是平衡测试计划的一部分,但它们可能会很慢。如果你希望实现测试驱动开发(我们就是这样做的),并快速测试新功能,那么就应该编写单元测试。

本文将介绍我们在加强单元测试计划方面所做的工作。

测试类型复习

在深入探讨如何在开发周期中添加单元测试之前,先来了解一下常见的测试类型。以下是如何定义不同类别的测试及其优点和缺点。

探索性测试:这类测试让质量保证工程师和测试人员专注于他们擅长的领域:发现边缘情况和错误。你可以给他们提供早期版本,让他们在上面敲敲打打,直到出现问题为止。测试人员不应该为每次变更/发布制定大量的手动回归测试计划。如果有一套成熟的 e2e、集成和单元测试,涵盖了回归部分,那么就可以让测试人员通过发挥创造力来发现错误。

端到端(e2e):这些测试模拟真实用户如何与应用程序交互,因此需要完整的应用程序设置,包括网络、数据库和依赖关系。可以设置这些测试的模拟版本,但通常他们会使用真实版本。当 e2e 测试通过时,就可以高度确信应用程序能按预期运行--至少对于正常路径操作、边缘情况和错误来说,在 e2e 中测试需要大量工作。但缺点是,由于 e2e 测试涉及整个应用程序,因此可能既慢又不稳定。

集成测试:这些测试功能如何与其依赖项协同工作。这些测试并不涵盖整个应用程序,而且是自动化的。与 e2e 测试一样,可以使用模拟和存根来防止向客户发送电子邮件等操作,但集成测试的重点是测试功能如何与依赖项协同工作,因此在可能的情况下应考虑使用真实的功能。集成测试可以测试 SQL 查询等操作,而这些操作在不访问依赖项的情况下是无法测试的。但是,任何测试依赖关系的方法都可能比较缓慢和不稳定。

单元测试:关于单元测试到底是什么,还存在一些争议,这里定义为单元测试是一种自动测试,它不与进程外的依赖关系对话;测试最小的一段代码,以确保其功能正确。它只测试单个进程,不测试其他任何内容。单元测试速度快,独立于应用程序中的任何其他部分运行。但缺点是,单元测试只测试单个功能,因此可以想象所有单元测试都通过了,而整个功能却被破坏了。如果测试过于接近功能的实现,维护起来就会很繁琐。

对我们来说,最大的弊端是历史架构使得编写单元测试非常困难。最佳测试实践表明,我们应该有大量的单元测试、中等数量的集成测试和少量的 e2e 测试。

由于我们几乎没有单元测试,所以必须开始行动。

我们为什么需要单元测试?

你可能会问,为什么现在要添加单元测试--我们已经做到了这一步,而且做得很好,不是吗?

正如之前提到的,作为一个工程组织,我们正在走向成熟,且拥有大型企业出高价购买的付费产品。因为在未来几年的路线图上有大量新技术投资,因此需要一个有弹性的代码库,能在必要时重构代码。换句话说,它能让我们快速行动,而不会破坏东西。此外,为测试重构代码还能创建一个干净的代码基准,并对未来的代码执行 "整洁规则":让代码保持整洁,甚至比你发现它时更整洁。

除了代码方面的好处,重构还能让整体测试程序变得更好,花费的时间更少。过去,在手动回归测试和拉取请求审查期间的手动测试上花费了大量时间。将这些测试自动化为单元测试,将释放开发人员的大量时间。这将更接近于测试驱动开发,从而能够继续为 Stack Overflow for Teams 产品的所有三个版本和社区网站提供新功能,即使这些功能需要修改现有代码。

我们的工作

为了创建真正的单元测试,需要确保任何功能都能与其依赖关系隔离开来。由于在公共网站和 Stack Overflow for Teams 实例上发生的几乎所有事情都会从数据库中提取数据,因此需要一种方法来向测试指示何时提取模拟数据。使用Dapper和 .NET 中的Entity Framework来管理数据库连接,来创建了一个扩展DbContext的接口,以便将模拟数据视为数据库连接。

Stack Overflow 会重复执行大量相同的查询。由于网站是为了提高速度而创建的,因此在 Entity Framework 中编译了大量此类查询。根据 DbContext 接口编译查询有点问题,因为 EF.CompileQuery 需要一个 DbContext 的具体实例。我们想出了一个辅助类,这样能在使用真实数据库时可以轻松地使用编译查询,而在运行单元测试时则可以使用内存查询。查询保持完全相同,因此这样验证了测试的是正确的行为。

一旦能够连接到模拟数据库,就需要提供一种方法来创建作为测试一部分的模拟数据。因此,这里引入了一个构建器,它可以为测试创建模拟网站数据。使用构建器而不是构造器,这样就可以改变这些模拟网站的构建方式,而不必重写所有的单元测试。构建器只通过显式传递所需的信息来构建对象,其他一切都使用默认值。同样,不想将测试与实现紧密结合在一起,因此选择尽可能抽象对象的构造。

一百多个 Stack Exchange 站点和 Teams 实例共享大量代码,但内容和设计可能有所不同。这些差异由站点设置控制,站点设置是一个智能配置存储,可以扩展到数以万计的站点而不会占用太多内存。要做到这一点,需要数据库连接,因此也需要在这方面做一些改动。为集成测试设置了一个模拟设置,但它的互耦程度非常高。在大多数其他代码钩子之前设置了一个异步上下文感知注入步骤,这样独立运行的测试就可以在不使用数据库的情况下初始化自定义模拟设置。这样做的另一个好处是,并行运行的测试不再改变同一套模拟设置,从而解决了一些不稳定问题。

我们还希望能够测试前端代码。为此,使用了 JavaScript/TypeScript 生态系统中最流行的测试库之一Jest。JS/TS 也有其他可靠的测试库,其中最著名的是Mocha,但在 Stacks 编辑器中使用 Jest 时更方便,因此我们决定将它用于Stacks所有的前端代码。

在这一点上,可以开始编写测试。基于这些变化,在 Stack Overflow for Teams 实例中建立了一个测试说明书,详细介绍了如何编写良好的单元测试和集成测试、从数据库模拟数据以及缓存。

好测试造就好代码

编写一个好的单元测试并不难。编写好的、可测试的代码才是难点。实现可测试代码的最佳方法包括编写无依赖性的纯功能代码。这在现代网络应用程序中是不可能实现的。第二个最好的方法是刻意注入依赖关系。过去,从静态上下文访问大量对象,而不是刻意传递它们,这使得创建可测试版代码变得非常困难。

有了它,就可以致力于可测试性、编写弹性代码,更重要的是,快速实现客户和社区需要的新功能。我们也在不断成长,这意味着代码质量变得越来越重要。自动化单元测试和可测试代码有助于实现所有这些目标。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末自行领取】

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

功耗或600W,RTX 5090、5080集体降级国内特供版

早在一个多月前,国外知名硬件泄密者 kopite7kimi 就曾透露 NVIDIA RTX 50 系显卡或推迟到明年初 CES 大会上发布。 来源:X 结合前几天爆出的台积电 4NP 工艺产能告急、NVIDIA 为提升下代显卡良率重新流片等。 如果不出意外,这原定在今年第四…

Π-系上的最小 d-系等于 Π-系上的最小集代数

称空间 Ω \Omega Ω 中满足下述条件的集系为 d d d-系: Ω ∈ D \Omega \in \mathscr{D} Ω∈D若 A , B ∈ D A, B \in \mathscr{D} A,B∈D 且 A ∩ B ϕ A \cap B\phi A∩Bϕ, 则 A B ∈ D AB \in \mathscr{D} AB∈D;若 A ⊂ B , A , B ∈ D A \subset B, A, B \in \…

visio 2021入门直通车(一天全搞定)

安装Visio 2021 (64bit)安装教程 1.1. 模板类型 1.2. 界面布局 1.3. 插入对象 1.4. 添加页面 1.5. 全屏演示|页面自适应|visio文件切换 1.6. 快捷键 快捷键说明 Shift 鼠标滚轮 按下shift,点击鼠标滚轮水平页面滚动 鼠标滚轮 垂直页面滚动 Ctrl 鼠标滚轮 按…

每天分享一个FPGA开源代码(6)- 浮点数运算

FPGA(现场可编程门阵列)是一种高度可配置的集成电路,它可以用于实现各种数字信号处理任务,包括浮点数运算。 在FPGA上进行浮点数运算通常涉及以下几个步骤: 1. 选择浮点数格式 浮点数运算首先要确定使用哪种浮点数格…

百度amis框架经验分享

百度amis框架经验分享 官方文档 amis - 低代码前端框架 这篇文章讲了amis的设计 为什么说百度AMIS框架是一个优秀的设计_百度前端框架-CSDN博客 学习方法: 最好的学习方法就是GPT官方文档 不要去很大力气通读官方文档,大概浏览一遍就行, 以你…

docker中搭建nacos并将springboot项目的配置文件转移到nacos中

前言 网上搜索docker中搭建nacos发现文章不是很好理解,正好最近在搭建nacos练手。记录一下整个搭建过程。 docker中搭建nacos并将springboot项目的配置文件转移到nacos中 前言1 docker中下拉nacos镜像2 配置nacos信息1. 创建docker的挂载目录,实现数据的…

SpringBoot之登录校验关于JWT、Filter、interceptor、异常处理的使用

什么是登录校验? 所谓登录校验,指的是我们在服务器端接收到浏览器发送过来的请求之后,首先我们要对请求进行校验。先要校验一下用户登录了没有,如果用户已经登录了,就直接执行对应的业务操作就可以了;如果用…

机器学习西瓜书笔记(十一) 第十一章特征选择与稀疏学习+代码

第十一章 特征选择与稀疏学习11.1 子集搜索与评价小结 11.2 过滤式选择小结 11.3 包裹式选择小结 11.4 嵌入式选择与L1正则化小结 11.5 稀疏表示与字典学习小结 11.6 压缩感知小结 11.7 代码单变量特征选择 11.8 章末小结 特征选择与稀疏学习 11.1 子集搜索与评价 小结 子集搜…

Facebook Marketplace无法使用的原因及解决方案

Facebook Marketplace是一项广受欢迎的买卖平台,然而,有时候用户可能会遇到无法访问或使用该功能的问题。通常,这些问题可以归结为以下几类原因: 地理位置限制: Facebook Marketplace并非在全球每个地区都可用。在某些…

【C++笔试强训】如何成为算法糕手Day2

学习编程就得循环渐进,扎实基础,勿在浮沙筑高台 循环渐进Forward-CSDN博客 目录 循环渐进Forward-CSDN博客 第一题:牛牛的快递 第二题:最小花费爬楼梯 第三题:数组中两个字符串的最小距离 补充0x3f3f3f3f 第一题…

OpenWrt学习(二)

OpenWrt是基于Linux系统进行开发需要学习Linux系统基本知识。下面介绍一下OpenWrt系统下常用的指令。 时间命令 查看当前时间 date查看当前日历 cal关机和重启 立即安全关闭系统 shutdown -h now 嵌入式设备不会关机,只会停止运行系统。 重新启动系统 reb…

内核是如何发送数据包

1、网络发包总览 网络发包总流程图如下: 从上图中可以看到用户数据被拷贝到内核态,然后经过协议栈处理后进入RingBuffer。随后网卡驱动真正的将数据发送了出去。当发送完成的时候,是通过硬中断来通知CPU,然后清理RingBuffer。 …

2024.9.25 作业和思维导图

栈 #include <iostream> #include <stdexcept> using namespace std;class My_stack { private:int * data; //栈空间int capacity;int top; //栈顶元素的下标 protected:public:/******************成员函数*************///构造函数My_stack(int c 10):capac…

JS中的事件和DOM操作

一、事件[重要] 1、 事件介绍 事件: 就是发生在浏览器(页面)上一件事,键盘事件,鼠标事件,表单事件,加载事件等等 2、 事件绑定方式 事件要想发生,就得将事件和标签先绑定(确定哪个标签发生什么事情,又有什么响应) 一个完整的事件有三部分 事件源(标签),哪里发出的事. 什么事(…

【DAY20240925】随机梯度下降:高效优化背后的原理与进阶策略

文章目录 前言随机梯度下降SGDMini-batch 随机梯度下降常见优化算法的改进版本 前言 梯度下降更新的通用形式&#xff1a; 论文中类似的表达形式&#xff0c;都表示根据 损失函数对这些参数的梯度 进行更新参数。梯度值较大时&#xff0c;说明当前控制参数对损失有较大的影响…

排序个人总结

插入排序 思路&#xff1b;定义 i 和 j&#xff0c;默认 i 前面的数都是有序的&#xff0c;j 定义为 i 的前一个数&#xff0c;把 i 的值给tmp&#xff0c;tmp与j对应的值进行比较&#xff0c;如果arr[j] > tmp,将arr[j] (大的数前移一位)&#xff0c;如下图 代码&#xf…

【亲子英语】英语故事有声绘本分享

文章目录 一、视觉与听觉的双重盛宴二、语言学习的最佳伙伴三、亲子共读的温馨时光四、适用人群广泛&#xff0c;随时随地学习五、获取方式 在这个快速发展的时代&#xff0c;英语学习已经不再局限于课本和课堂。特别是对于活泼好动的孩子们来说&#xff0c;一种既有趣又高效的…

open-resty 服务安装jwt插件

作者&#xff1a;程序那点事儿 日期&#xff1a;2023/11/16 22:07 lua-resty-jwt 插件 如果想使用Lua识别用户令牌&#xff0c;我们需要引入lua-resty-jwt模块&#xff0c;是用于 ngx_lua 和 LuaJIT 的 Lua 实现库&#xff0c;在该模块能实现Jwt令牌生成、Jwt令牌校验。 下载…

9.25作业

手动实现队列 代码如下 MyQueue.h #ifndef MYQUEUE_H #define MYQUEUE_H #include <iostream> #include <cstring> using namespace std;class Queue{ private:char* data; //字符串数据int len; //当前数量int size; //最大容量int front; //头索引int …

uboot — uboot命令的使用

uboot的命令繁多&#xff0c;下文只对工作中常用到的命令进行记录&#xff0c;其余命令待用到时再查查资料也不迟 一、环境变量操作命令 1、printenv 打印环境变量 2、setenv 修改环境变量/新建环境变量 3、saveenv 保存环境变量/删除环境变量&#xff08;给环境变量赋空值…