极客时间已完结课程限时免费阅读

21|什么是测试工序?

21|什么是测试工序?-徐昊 · AI时代的软件工程-极客时间
下载APP

21|什么是测试工序?

讲述:徐昊

时长08:15大小7.56M

你好,我是徐昊,今天我们来继续学习 AI 时代的软件工程。
上节课,我们按照测试策略的指导,遵循前面讲过的测试驱动开发的节奏,完成了一个简单的功能。这个过程中,我们生成的代码符合项目中既有的架构规则。
我们上节课所采用的方法,就是一个按照测试工序完成编码的例子。今天我们就来讲讲什么是测试工序,以及我们要如何使用它。

测试工序

工序是指完成特定任务或生产产品所需的一系列步骤或程序。在制造业、生产领域或项目管理中,工序通常用于描述完成特定工作的方法或步骤。每个工序都有其独特的目标、方法和所需的资源。例如,在制造产品时,工序可以包括原材料的采购、加工、装配和质量控制等步骤。在项目管理中,工序则可以涵盖项目的规划、执行、监控和收尾等阶段。工序的定义和执行对于确保工作的有效进行和产品的质量至关重要。
而对于软件开发,工序由测试策略定义。正如前一节课学到的,我们按照测试策略的指引,逐步完成架构中不同组件的开发与集成。这个过程中,测试策略制定了我们所需完成的任务。因此,软件开发中的工序也叫测试工序
让我们再来看一下上节课的例子。
在这个例子中,我们定义了四个测试工序:
使用 Fake 数据库,测试 Persistent 层中的组件;
通过 Stub Persistent 层的组件,测试 Application Logic 层中的组件;
通过 Stub Application Logic 层的组件,测试 HTTP Interface 层中的组件;
使用独立的测试数据库,对三层组件进行功能测试。
无论对于哪个需求,我们都能按照上述工序完成。比如,当我们要面对新需求“按 SKU 商品查询”时,我们可以先按照业务场景,对于商品查询进行场景分解:
当查询的商品存在时,返回查到的商品;
当查询的商品不存在时,返回 404。
然后,我们就可以在不同场景下,按照测试工序对场景进行进一步的分解:
当查询的商品存在时,返回查到的商品。
a. Fake 数据库,测试从数据库中查询的 DAO(工序 1);
b. Stub DAO,测试 Application Logic(工序 2);
c. Stub Application Logic,测试 API 返回 200,以及正确的数据(工序 3);
d 功能测试(工序 4);
当查询的商品不存在时,返回 404。
a. Fake 数据库,测试从数据库中查询的 DAO(工序 1,同查询商品存在时的工序 1);
b. Stub DAO,测试 Application Logic(工序 2);
c. Stub Application Logic,测试 API 返回 404(工序 3);
d. 功能测试(工序 4);
对于其他需求,我们也可以进行类似的分解。需要注意的是,虽然我们最终完成了架构中规定的组件,但我们主要是按照测试策略划分的工序。同样对于上面的例子,如果我们的测试策略发生了改变。比如,因为测试成本或是因为所选用的工具 / 框架存在特殊的限制,我们改变了测试策略:
在新的测试策略下,我们定义了三个测试工序:
使用 Fake 数据库,测试 Persistent 层中的组件;
通过 Stub Persistent 层的组件,测试 HTTP Interface 层和 Application Logic 层中的组件;
使用独立的测试数据库,对三层组件进行功能测试。
那么,当我们要面对新需求“按 SKU 商品查询”时,我们的任务分解就变成了这个样子:
1. 当查询的商品存在时,返回查到的商品。
a. Fake 数据库,测试从数据库中查询的 DAO(工序 1);
b. Stub DAO,测试 API 返回 200,以及正确的数据(工序 2);
c. 功能测试(工序 3);
2. 当查询的商品不存在时,返回 404。
a. Fake 数据库,测试从数据库中查询的 DAO(工序 1,同 1a);
b. Stub DAO,测试 API 返回 404(工序 2);
c. 功能测试(工序 3);
可以看到虽然架构中规定的组件没有改变,但是因为选择了不同的测试策略,就会到得完全不同的工序。
看到这里,你可能会有疑问,为什么我们不直接从架构上去设计工序,而要依赖于测试策略?这是因为,对于今日的软件开发,可测试性是进程内架构最重要的属性。

可测试性是进程内架构的最重要属性

大部分进程内架构模式的引入,都意味着在可测试性上的改进。下面让我们看一个例子。
以前端开发为例,假设我们现在不使用任何框架,直接使用 JavaScript 和 HTML 进行开发。我们默认使用的架构模式是 MVC(Model-View-Controller)架构模式。它将应用程序分成三个主要部分:
模型(Model):模型代表应用程序的数据和业务逻辑。它负责处理数据的存储、检索、更新和验证。在 MVC 架构中,模型通常独立于用户界面,这意味着它可以用于不同的用户界面或应用程序;
视图(View):视图是用户界面的呈现部分,负责向用户显示数据,并接收用户的输入。它通常包括用户界面元素,如按钮、文本框、图表等。视图负责呈现模型的数据,但不直接处理数据。对于我们的应用而言,视图就是 HTML DOM;
控制器(Controller):控制器是模型和视图之间的中介。它会接收用户的输入(通常来自视图),然后根据输入更新模型或者调用适当的视图来呈现数据。控制器负责应用程序的流程控制和业务逻辑。对于我们的应用而言,控制器就是挂在 HTML DOM 上,监听并响应事件的事件处理逻辑。
由于 HTML DOM 和它上面的事件都难以测试,从实际情况出发,很多团队选择不去对它们进行测试。那么我们当前架构下的测试策略可能是这样的:
这个测试策略显然存在很大的隐患,容易出错。虽然功能测试覆盖(Q2)了大量代码聚集的视图和控制器部分,但没有考虑到针对前端的功能测试成本很高(运行慢,测试不稳定等等)。Q1 测试过于集中在不易出错且容易测试的模型部分,对于前端反而缺少有效的 Q1 测试来降低成本,提高定位准确性。因而整体测试的投资回报率不高。
一个很容易想到的改进办法,就是使用 MVP(Model-View—Presenter)架构模式,来改造这个应用。MVP 架构模式是一种软件设计模式,用于开发用户界面。它是一种演变自经典 MVC(Model-View-Controller)模式的设计范式。MVP 模式的核心组成部分包括:
模型(Model):模型代表应用程序的数据和业务逻辑。它负责处理数据的存储、检索和修改,以及应用程序的核心功能。
视图(View):视图是用户界面的展示部分,负责展示数据并向用户传达信息。它通常是由用户直接与之交互的部分。
Presenter:Presenter 是模型和视图之间的中介,负责处理用户输入、更新模型数据并更新视图。Presenter 从模型中检索数据,并将其转换为视图可以理解和展示的形式。Presenter 还接收来自视图的用户输入,对其进行处理并相应地更新模型。
因而我们可以得到新的架构:
MVP 架构模式引入了不同的组件,我们就有不同的测试策略可以选择了。通常而言,对于视图(View)的测试成本很高。一种流行的测试策略是,忽略对于视图的测试,将测试的重点集中在 Presenter 上。毕竟 Presenter 中封装的是交互逻辑。那么,我们可以通过 Stub 或使用 Fake 的 Model,对 Presenter 进行测试。于是我们可以得到以下的测试策略:
对比 MVC 的架构模式,我们缩小了没有 Q1 测试覆盖的组件范围(View+Controllers 到 View)。使 Q1 测试更多覆盖到逻辑密集的部分(Presenter)。单从测试的角度上来看,就能知道,在当前情况下,使用 MVP 架构模式能更好地提高交付质量。
当然,我们还可以更进一步。针对视图不好测试的问题,行业也在努力寻找解决方案,也出现了新的工具,比如类似于 React Testing Library,Storybook Component Test 或者 Ladle 等等。
那么,当我们选择使用这些工具时,我们可以将测试的重点放在 View 和 Presenter 上,通过 Stub 或 Fake Model 模拟不同的场景,完成对于 View 和 Presenter 的测试,并以此代替功能测试。于是我们可以得到以下的测试策略:
这显然是一个投资回报率更好的测试策略。因而,我们不需要考虑那么多虚妄的 *-abilities,比如可扩展性等等。单从可测试性角度上来看,我们就能知道,在当前的场景下,MVP 是比 MVC 更好的架构模式。而这些,都能从测试策略的改变上发现端倪。

小结

工序是架构落地的重要手段。架构设计通常是在更高层次上进行的,它定义了系统的结构和组件之间的关系,以及如何满足系统的需求和非功能性属性。然而,要使得架构设计真正生效,需要通过具体的步骤和方法将其实现并落地到实际的开发过程中,而工序就是实现这一目标的关键手段之一。
工序定义了完成特定任务或生产产品所需的一系列步骤或程序。在软件开发中,工序可以指导开发团队按照预定的流程和方法进行开发工作,确保架构设计的理念和原则得以贯彻执行。通过合理的工序,开发团队可以有序地进行系统开发、集成、测试和部署,从而确保最终交付的软件系统符合设计要求、具备良好的质量和可靠性。
工序在架构落地过程中扮演着重要的角色,它有助于将抽象的架构设计转化为具体的开发任务和实际的工作流程,帮助团队有效地实现架构设计,并最终产生质量高、可靠性强的软件系统。
下节课,我们将会学习如何使用 LLM 帮助我们应用工序。

思考题

请将你团队中的架构,按照测试策略转化为工序。
欢迎在留言区分享你的想法,我会让编辑置顶一些优质回答供大家学习讨论。

1. 测试工序是软件开发中的关键步骤,对于确保产品质量至关重要。 2. 可测试性是进程内架构的重要属性,架构模式的引入意味着在可测试性上的改进。 3. 选择合适的架构模式可以影响测试策略的选择,从而提高交付质量和测试的投资回报率。 4. MVP架构模式相比MVC架构模式能更好地提高交付质量,需要选择合适的测试策略来应对架构中不易测试的部分。 5. 使用新的工具和技术,如React Testing Library、Storybook Component Test等,可以改进测试策略,提高测试的投资回报率。 6. 选择合适的测试策略可以帮助团队更好地应对测试成本、稳定性等挑战,从而提高软件产品的质量和可靠性。

分享给需要的人,Ta购买本课程,你将得29
生成海报并分享
2024-04-24

赞 2

提建议

上一篇
20|使用 LLM 按照测试策略生成代码
下一篇
22|通过测试工序提高LLM代码质量
unpreview
 写留言

全部留言(6)

  • 最新
  • 精选
  • 范飞扬
    2024-05-09 来自广东
    老师的测试工序的顺序是由内而外的,我觉得,多少有点误导性: 1、老师自己在《TDD》课里就说,伦敦学派是“按照调用栈(Call Stack)的顺序,自外向内依次实现不同的对象”,所以,这和目前测试工序的顺序就矛盾了,现在文中的顺序是从持久层开始,是自内向外。 2、当然,你也可以强行说,老师这个工序用的是经典学派TDD。但是,经典学派一般用于功能上下文内,或是没有架构的情况。显然,这测试工序不是在讨论功能上下文内的事,而且你也都有架构了,那还用经典学派干什么呢?自内而外来实现很坑的(因为我踩过...)。为什么坑?详见伦敦学派的经典著作《Growing object-oriented software guided by tests》,这里我摘录一段,翻译和原文都有: (GPT翻译)“开始时进行新领域模型对象的单元测试,然后尝试将它们集成到应用程序的其他部分,这似乎很有吸引力。一开始看起来更简单——当我们不需要让它适应任何东西时,我们感觉在领域模型上的工作进展迅速——但是,我们更有可能在稍后遇到集成问题。因为在开发过程中我们没有得到正确类型的反馈,所以我们将浪费时间构建不必要的或错误的功能。” (原文) “It’s tempting to start by unit-testing new domain model objects and then trying to hook them into the rest of the application. It seems easier at the start—we feel we’re making rapid progress working on the domain model when we don’t have to make it fit into anything—but we’re more likely to get bitten by integration problems later. We’ll have wasted time building unnecessary or incorrect func-tionality, because we weren’t receiving the right kind of feedback when we were working on it.”
    展开

    作者回复: 分层架构再理解一下

    共 3 条评论
    2
  • 术子米德
    2024-04-24 来自浙江
    🤔☕️🤔☕️🤔 【R】测试工序 由 测试策略定义,完成由架构定义的组件开发与集成过程中,测试所需要完成的任务。 【.I.】架构设计回答,我有这样的人才可以干活、我要达到这样的内建质量,测试策略回答,我也只有这样的人可以干活、我要最佳ROI效果,架构和测试同时要回答,未来潜在的变化在哪里,预期的ROI变化会往哪里走。工序,就是当下架构和测试,满足ROI所呈现出来的样子。 【Q】测试策略确定、架构组件确定,测试工序自然确定,独立说测试工序的特殊目的和意义何在? — by 术子米德@2024年4月24日
    展开

    作者回复: 并没有你想的那么自然 测试工序是从知识消费者角度出发的形态

  • 范飞扬
    2024-04-25 来自广东
    原文 === 工序是指生产产品所需的一系列步骤或程序。对于软件开发,工序由测试策略定义。 思考 ==== 为什么“软件开发的工序”由测试策略定义? 换一个方式来问,钟敬老师在他的《DDD》课里说 DDD 的基本开发流程是:“捕获行为需求、领域建模、架构设计、数据库设计、代码实现”,为什么“软件开发的工序”不是这个“DDD 的基本开发流程”,而是“由测试策略定义”? 我觉得,老师《TDD》课的开篇词可以回答这个问题。开篇词说:验证测试与定位测试,本身就贯穿了整个软件构造的过程。测试构成了整个开发流程的骨架,功能开发可以看作填充在测试与测试之间的血肉。 所以,既然是骨架,那么软件开发的工序由测试策略定义,就很自然了。 下面是开篇词的原文: 我们构造软件的过程,就是通过一系列验证测试(测试应用、跑一下等),证明我们在朝着正确的方向前进;如果验证的过程中发现出了错误,那么再通过一系列定位测试(调试等),找到问题的根源,加以改进。如此往复,直到完成全部功能。 验证测试与定位测试,本身就贯穿了整个软件构造的过程。测试构成了整个开发流程的骨架,功能开发可以看作填充在测试与测试之间的血肉。 这就是测试驱动开发的核心逻辑:以测试作为切入点,可以提纲挈领地帮助我们把握整个研发的过程。一个个测试就像一个个里程碑点(Milestone),规划着研发活动的进度。围绕这些里程碑点,我们就可以持续对成本和效率进行管理和改进。
    展开
    共 1 条评论
    1
  • 范飞扬
    2024-05-09 来自广东
    老师之前讲,TDD 的任务列表是双分解(需求、架构)。现在看来,双分解还不够,得三分解(需求、架构、测试策略)。这也是为什么老师说任务分解很难吧...
  • 奇小易
    2024-04-25 来自福建
    终于了解了从架构中划分任务的具体思路,学TDD课程的时候总觉得差点什么。
  • aoe
    2024-04-25 来自浙江
    原来我都是在小作坊里打零工。 第一次有了工序的概念,离产生质量高、可靠性强的软件系统又近了一步。