从 PHPUnit 到 Go:Go 开发人员的数据驱动单元测试
本篇文章向大家介绍《从 PHPUnit 到 Go:Go 开发人员的数据驱动单元测试》,主要包括,具有一定的参考价值,需要的朋友可以参考一下。

在这篇文章中,我们将探讨如何将 php 单元测试思维,特别是 phpunit 框架的数据提供者方法引入 go。如果您是一位经验丰富的 php 开发人员,您可能熟悉数据提供程序模型:在原始数组中单独收集测试数据并将这些数据输入到测试函数中。这种方法使单元测试更干净、更易于维护,并遵守开放/封闭等原则。
为什么采用数据提供者方法?
使用数据提供者方法在 go 中构建单元测试具有多种优势,包括:
增强的可读性和可扩展性:测试变得可视化组织,顶部有清晰分隔的数组代表每个测试场景。每个数组的键描述场景,而其内容保存测试该场景的数据。这种结构使文件易于处理并且易于扩展。
关注点分离:数据提供者模型将数据和测试逻辑分开,从而产生一个轻量级、解耦的函数,随着时间的推移,该函数可以基本保持不变。添加新场景只需要向提供者追加更多数据,保持测试功能对扩展开放,对修改关闭——开放/封闭原则在测试中的实际应用。
在某些项目中,我什至看到了足够密集的场景,足以保证使用单独的 json 文件作为数据源,手动构建并提供给提供程序,而提供程序又向测试函数提供数据。
什么时候非常鼓励使用数据提供者?
当您有大量具有不同数据的测试用例时,特别鼓励使用数据提供程序:每个测试用例在概念上相似,但仅在输入和预期输出方面有所不同。
在单个测试函数中混合数据和逻辑会降低开发人员体验 (dx)。它通常会导致:
冗长过载:重复具有轻微数据变化的语句的冗余代码,导致代码库冗长而没有额外的好处。
清晰度降低:当尝试将实际测试数据与周围代码隔离时,扫描测试函数变得很麻烦,而数据提供程序方法自然可以缓解这种情况。
很好,那么数据提供者到底是什么?
phpunit 中的 dataprovider 模式,基本上提供程序函数为测试函数提供在隐式循环中使用的不同数据集。它确保了 dry(不要重复自己)原则,并与开放/封闭原则保持一致,使得在不改变核心测试功能逻辑的情况下更容易添加或修改测试场景。
在没有数据提供者的情况下解决问题?
为了说明冗长、代码重复和维护挑战的缺点,下面是在没有数据提供者帮助的情况下对冒泡排序函数进行单元测试的示例片段:
assertsame([], bubblesort([]));
}
public function testbubblesortoneelement()
{
$this->assertsame([0], bubblesort([0]));
}
public function testbubblesorttwoelementssorted()
{
$this->assertsame([5, 144], bubblesort([5, 144]));
}
public function testbubblesorttwoelementsunsorted()
{
$this->assertsame([-7, 10], bubblesort([10, -7]));
}
public function testbubblesortmultipleelements()
{
$this->assertsame([1, 2, 3, 4], bubblesort([1, 3, 4, 2]));
}
// and so on for each test case, could be 30 cases for example.
public function testbubblesortdescendingorder()
{
$this->assertsame([1, 2, 3, 4, 5], bubblesort([5, 4, 3, 2, 1]));
}
public function testbubblesortboundaryvalues()
{
$this->assertsame([-2147483647, 2147483648], bubblesort([2147483648, -2147483647]));
}
}
上面的代码有问题吗?当然:
冗长:每个测试用例都需要一个单独的方法,从而导致大量重复的代码库。
重复:测试逻辑在每个方法中重复,仅根据输入和预期输出而变化。
开放/封闭违规:添加新的测试用例需要通过创建更多方法来改变测试类结构。
解决数据提供者的问题!
这是使用数据提供者重构的相同测试套件
*/
public function bubblesortdataprovider(): array
{
return [
'empty' => [[], []],
'oneelement' => [[0], [0]],
'twoelementssorted' => [[5, 144], [5, 144]],
'twoelementsunsorted' => [[10, -7], [-7, 10]],
'morethanoneelement' => [[1, 3, 4, 2], [1, 2, 3, 4]],
'morethanoneelementwithrepetition' => [[1, 4, 4, 2], [1, 2, 4, 4]],
'morethanoneelement2' => [[7, 7, 1, 0, 99, -5, 10], [-5, 0, 1, 7, 7, 10, 99]],
'sameelement' => [[1, 1, 1, 1], [1, 1, 1, 1]],
'negativenumbers' => [[-5, -2, -10, -1, -3], [-10, -5, -3, -2, -1]],
'descendingorder' => [[5, 4, 3, 2, 1], [1, 2, 3, 4, 5]],
'randomorder' => [[9, 2, 7, 4, 1, 6, 3, 8, 5], [1, 2, 3, 4, 5, 6, 7, 8, 9]],
'duplicateelements' => [[2, 2, 1, 1, 3, 3, 4, 4], [1, 1, 2, 2, 3, 3, 4, 4]],
'largearray' => [[-1, -10000, -12345, -2032, -23, 0, 0, 0, 0, 10, 10000, 1024, 1024354, 155, 174, 1955, 2, 255, 3, 322, 4741, 96524], [-1, -10000, -12345, -2032, -23, 0, 0, 0, 0, 10, 10000, 1024, 1024354, 155, 174, 1955, 2, 255, 3, 322, 4741, 96524]],
'singlenegativeelement' => [[-7], [-7]],
'arraywithzeroes' => [[0, -2, 0, 3, 0], [-2, 0, 0, 0, 3]],
'ascendingorder' => [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]],
'descendingorderwithduplicates' => [[5, 5, 4, 3, 3, 2, 1], [1, 2, 3, 3, 4, 5, 5]],
'boundaryvalues' => [[2147483648, -2147483647], [-2147483647, 2147483648]],
'mixedsignnumbers' => [[-1, 0, 1, -2, 2], [-2, -1, 0, 1, 2]],
];
}
/**
* @dataprovider bubblesortdataprovider
*
* @param array $input
* @param array $expected
*/
public function testbubblesort(array $input, array $expected)
{
$this->assertsame($expected, bubblesort($input));
}
}
使用数据提供者有什么优势吗?哦,是的:
简洁:所有测试数据都集中在一个方法中,无需针对每个场景使用多个函数。
增强可读性:每个测试用例都组织良好,每个场景都有描述性的键。
开放/封闭原则:可以在不改变核心测试逻辑的情况下向数据提供者添加新案例。
改进的 dx(开发人员体验):测试结构干净、吸引人,甚至让那些懒惰的开发人员也有动力去扩展、调试或更新它。
让数据提供商走上正轨
- go 没有像 phpunit 这样的原生数据提供者模型,因此我们需要使用不同的方法。可能有多种复杂程度的实现,以下是一个平均的实现,可能是在 go 中模拟数据提供者的候选者
package sort
import (
"testing"
"github.com/stretchr/testify/assert"
)
type TestData struct {
ArrayList map[string][]int
ExpectedList map[string][]int
}
const (
maxInt32 = int32(^uint32(0) >> 1)
minInt32 = -maxInt32 - 1
)
var testData = &TestData{
ArrayList: map[string][]int{
"empty": {},
"oneElement": {0},
"twoElementsSorted": {5, 144},
"twoElementsUnsorted": {10, -7},
"moreThanOneElement": {1, 3, 4, 2},
"moreThanOneElementWithRepetition": {1, 4, 4, 2},
"moreThanOneElement2": {7, 7, 1, 0, 99, -5, 10},
"sameElement": {1, 1, 1, 1},
"negativeNumbers": {-5, -2, -10, -1, -3},
"descendingOrder": {5, 4, 3, 2, 1},
"randomOrder": {9, 2, 7, 4, 1, 6, 3, 8, 5},
"duplicateElements": {2, 2, 1, 1, 3, 3, 4, 4},
"largeArray": {-1, -10000, -12345, -2032, -23, 0, 0, 0, 0, 10, 10000, 1024, 1024354, 155, 174, 1955, 2, 255, 3, 322, 4741, 96524},
"singleNegativeElement": {-7},
"arrayWithZeroes": {0, -2, 0, 3, 0},
"ascendingOrder": {1, 2, 3, 4, 5},
"descendingOrderWithDuplicates": {5, 5, 4, 3, 3, 2, 1},
"boundaryValues": {2147483648, -2147483647},
"mixedSignNumbers": {-1, 0, 1, -2, 2},
},
ExpectedList: map[string][]int{
"empty": {},
"oneElement": {0},
"twoElementsSorted": {5, 144},
"twoElementsUnsorted": {-7, 10},
"moreThanOneElement": {1, 2, 3, 4},
"moreThanOneElementWithRepetition": {1, 2, 4, 4},
"moreThanOneElement2": {-5, 0, 1, 7, 7, 10, 99},
"sameElement": {1, 1, 1, 1},
"negativeNumbers": {-10, -5, -3, -2, -1},
"descendingOrder": {1, 2, 3, 4, 5},
"randomOrder": {1, 2, 3, 4, 5, 6, 7, 8, 9},
"duplicateElements": {1, 1, 2, 2, 3, 3, 4, 4},
"largeArray": {-1, -10000, -12345, -2032, -23, 0, 0, 0, 0, 10, 10000, 1024, 1024354, 155, 174, 1955, 2, 255, 3, 322, 4741, 96524},
"singleNegativeElement": {-7},
"arrayWithZeroes": {-2, 0, 0, 0, 3},
"ascendingOrder": {1, 2, 3, 4, 5},
"descendingOrderWithDuplicates": {1, 2, 3, 3, 4, 5, 5},
"boundaryValues": {-2147483647, 2147483648},
"mixedSignNumbers": {-2, -1, 0, 1, 2},
},
}
func TestBubble(t *testing.T) {
for testCase, array := range testData.ArrayList {
t.Run(testCase, func(t *testing.T) {
actual := Bubble(array)
assert.ElementsMatch(t, actual, testData.ExpectedList[testCase])
})
}
}
- 我们基本上定义了两个映射/列表:一个用于输入数据,第二个用于预期数据。我们确保双方的每个案例场景都通过双方相同的映射键进行引用。
- 执行测试就是一个简单函数中的循环问题,该函数迭代准备好的输入/预期列表。
- 除了一些一次性的样板类型之外,对测试的修改应该只发生在数据方面,大多数情况下不应该改变执行测试的函数的逻辑,从而实现我们上面谈到的目标:测试工作归结为原始数据准备问题。
奖励:可以在此处找到实现本博文中呈现的逻辑的 github 存储库 https://github.com/medunes/dsa-go。到目前为止,它包含运行这些测试的 github 操作,甚至显示超级著名的绿色徽章;)
下一篇[希望]信息丰富的帖子中见!
今天带大家了解了的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
NumPy 中的 unique 函数为什么自动排序?
- 上一篇
- NumPy 中的 unique 函数为什么自动排序?
- 下一篇
- win10电脑以太网未启用dhcp怎么办 win10电脑以太网未启用dhcp问题解析
-
- Golang · Go教程 | 1天前 | Go教程 · Go encoding/xml 空元素 指针字段
- Go encoding/xml 空元素与指针字段的处理方式
- 142浏览 收藏
-
- Golang · Go教程 | 1天前 | Go教程 · Go encoding/xml 字段映射 Unmarshaler
- Go encoding/xml 自定义 Unmarshaler 的字段映射
- 189浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go encoding/xml Token 流式读取大型 XML
- 424浏览 收藏
-
- Golang · Go教程 | 2天前 | go · encoding/json · JSON Go encoding/json omitempty
- Go encoding/json omitempty 对零值字段的输出边界
- 192浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go encoding/json Decoder Token 流式读取嵌套结构
- 364浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go encoding/json Decoder UseNumber 保留大整数精度
- 328浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go fmt.Scanner 自定义扫描规则的实现要点
- 182浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go fmt.Appendf 追加格式化结果的低分配写法
- 478浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go strings.IndexByte 定位协议分隔符的低分配写法
- 413浏览 收藏
-
- Golang · Go教程 | 2天前 | go · Strings · Go 字符串前缀 strings.CutPrefix
- Go strings.CutPrefix 处理可选前缀的分支设计
- 165浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 280次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 334次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 329次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 299次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 120次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览
