在这篇文章中,我们将探索如何将 PHP 单元测试思维,特别是 PHPUnit 框架的数据提供者方法引入 Go。如果您是一位经验丰富的 PHP 开发人员,您可能熟悉数据提供程序模型:在原始数组中单独收集测试数据并将这些数据输入到测试函数中。这种方法使单元测试更干净、更易于维护,并遵守开放/封闭等原则。
使用数据提供者方法在 Go 中构建单元测试具有多种优势,包括:
增强的可读性和可扩展性:测试变得可视化组织,顶部有清晰分隔的数组代表每个测试场景。每个数组的键描述场景,而其内容保存测试该场景的数据。这种结构使文件易于处理并且易于扩展。
关注点分离:数据提供者模型将数据和测试逻辑分开,从而产生一个轻量级、解耦的函数,随着时间的推移,该函数可以基本保持不变。添加新场景只需要向提供者追加更多数据,保持测试功能对扩展开放,对修改关闭——开放/封闭原则在测试中的实际应用。
在某些项目中,我什至看到了足够密集的场景,足以保证使用单独的 JSON 文件作为数据源,手动构建并提供给提供程序,而提供程序又向测试函数提供数据。
当您有大量具有不同数据的测试用例时,特别鼓励使用数据提供程序:每个测试用例在概念上相似,但仅在输入和预期输出方面有所不同。
在单个测试函数中混合数据和逻辑会降低开发人员体验 (DX)。它通常会导致:
冗长过载:重复具有轻微数据变化的语句的冗余代码,导致代码库冗长而没有额外的好处。
清晰度降低:当尝试将实际测试数据与周围代码隔离时,扫描测试函数变得很繁琐,而数据提供程序方法自然可以缓解这种情况。
PHPUnit 中的 DataProvider 模式,基本上提供程序函数为测试函数提供在隐式循环中使用的不同数据集。它确保了 DRY(不要重复自己)原则,并与开放/封闭原则保持一致,使得在不改变核心测试功能逻辑的情况下更容易添加或修改测试场景。
为了说明冗长、代码重复和维护挑战的缺点,下面是在没有数据提供者帮助的情况下对冒泡排序函数进行单元测试的示例片段:
<?php declare(strict_types=1); use PHPUnit\Framework\TestCase; final class BubbleSortTest extends TestCase { public function testBubbleSortEmptyArray() { $this->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])); } }
上面的代码有问题吗?当然:
冗长:每个测试用例都需要一个单独的方法,从而导致大量重复的代码库。
重复:测试逻辑在每个方法中重复,仅根据输入和预期输出而变化。
开放/封闭违规:添加新的测试用例需要通过创建更多方法来改变测试类结构。
这是使用数据提供者重构的相同测试套件
<?php declare(strict_types=1); use PHPUnit\Framework\TestCase; final class BubbleSortTest extends TestCase { /** * Provides test data for bubble sort algorithm. * * @return array<string, array> */ 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<int> $input * @param array<int> $expected */ public function testBubbleSort(array $input, array $expected) { $this->assertSame($expected, BubbleSort($input)); } }
使用数据提供者有什么优势吗?哦,是的:
简洁:所有测试数据都集中在一个方法中,无需针对每个场景使用多个函数。
增强可读性:每个测试用例都组织良好,每个场景都有描述性的键。
开放/封闭原则:可以在不改变核心测试逻辑的情况下向数据提供者添加新案例。
改进的 DX(开发人员体验):测试结构干净、吸引人,甚至让那些懒惰的开发人员也有动力去扩展、调试或更新它。
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 操作,甚至显示超级著名的绿色徽章;)
下一篇[希望]内容丰富的帖子中见!
以上是从 PHPUnit 到 Go:Go 开发人员的数据驱动单元测试的详细内容。更多信息请关注PHP中文网其他相关文章!