Typescript 测试驱动开发 TDD (11)
数据驱动测试 (Data-driven tests)
很多时候,我们需要运行相同的测试多次,只是输入值不同。作为这个的一个例子,请考虑以下测试:
[1, 2, 3, 4, 5] .forEach((value: number) => { it(`${value} should be less than 5`, () => { expect(value).toBeLessThan(5); }) });
在这里,我们定义了一个包含数字1到5的数组。然后,我们调用forEach函数,该函数接受一个函数作为参数,并且将对数组中的每个值执行一次该函数。请注意,在此forEach函数内部,我们定义了一个测试,并且该测试期望传入的值应小于5。还要注意,在实际测试名称中使用了模板字符串,以便通过值来区分这些测试。以下是此测试失败的情况:
在这里,我们可以看到失败的测试被命名为“5应该小于5”,而Jest告诉我们值5实际上不小于5。
以这种方式使用forEach给了我们一种快速设置一系列带有各种数据的测试的方法。现在让我们来看看如何为验证函数编写一些数据驱动的测试,其代码如下:
1 function hasValueNoWhiteSpace(value: string): boolean { 2 if ( 3 value && 4 value.length > 0 && 5 value.trim().length > 0 ) { 6 return true; 7 } 8 return false; 9 }
这里,我们有一个名为hasValueNoWhiteSpace的函数,它接受一个字符串参数,并返回一个布尔值。此代码正在检查输入值是否为空字符串,或者在修剪后是否为空字符串。要测试此函数,我们期望值" "将返回false,但值"a"将返回true。
然后我们的测试用例类似于以下内容:
1 [ 2 [undefined, false], 3 [null, false], 4 [" ", false], 5 [" ", false], 6 [" a ", true] 7 ]
在这里,我们定义了一个元组数组,每个元组有两个值。第一个值是我们想要测试的值,第二个值是hasValueNoWhiteSpace函数的预期结果。为了使用这种输入创建数据驱动测试,让我们编写一个小型实用函数如下:
1 function testUsing<T>(values: T[], func: Function) { 2 for (let value of values) { 3 func.apply(Object, [value]); 4 } 5 }
这里,我们有一个名为testUsing的函数,它可以与任何类型T一起使用。该函数有两个参数,分别是values和func。values参数是一个类型为T的数组,而func参数是一个回调函数。在testUsing函数内部,我们正在循环遍历数组的每个元素,并使用apply函数调用当前数组元素来调用func函数。现在我们可以按照以下方式使用这个testUsing函数:
1 testUsing( 2 [ 3 [undefined, false], 4 [null , false], 5 [" ", false], 6 [" ", false], 7 [" a ", true] 8 ] 9 , ([value, isValid]: [string, boolean]) => { 10 11 it(`"${value}" hasValueNoWhiteSpace ? ${isValid}`, 12 () => { 13 isValid ? 14 expect(hasValueNoWhiteSpace(value)).toBeTruthy() : 15 expect(hasValueNoWhiteSpace(value)).toBeFalsy(); 16 } 17 ); 18 });
在这里,我们使用两个参数调用testUsing函数。第一个参数是我们想要运行测试的值数组。第二个参数是一个函数,它将数组中的每个元组解构为名为value和isValid的变量。然后,我们调用it函数来创建一个包含数组中每个值的测试。这个数据驱动测试序列的输出如下:
在这里,我们可以看到testUsing函数正在对我们的数据数组中的每个值运行一个测试。根据每个元组的第二个值,该测试将确定hasValueNoWhiteSpace函数是否返回正确的布尔值。
数据驱动测试是一种方便的编写单元测试的方式,其中一系列测试唯一真正变化的是输入或结果值,但测试本身保持不变。