From 27bd7c55fdf25f2fe798e11dba8670ddf4aa433f Mon Sep 17 00:00:00 2001 From: chai2010 Date: Sat, 12 Dec 2015 16:44:04 +0800 Subject: [PATCH] Update ch11-02-1.md --- ch11/ch11-02-1.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/ch11/ch11-02-1.md b/ch11/ch11-02-1.md index 18c7097..513e501 100644 --- a/ch11/ch11-02-1.md +++ b/ch11/ch11-02-1.md @@ -1,3 +1,51 @@ ### 11.2.1. 隨機測試 -TODO + +表格驱动的测试便于构造基于精心挑选的测试数据的测试用例. 另一种测试思路是随机测试, 也就是通过构造更广泛的随机输入来测试探索函数的行为. + +那么对于一个随机的输入, 我们如何能知道希望的输出结果呢? 这里有两种策略. 第一个是编写另一个函数, 使用简单和清晰的算法, 虽然效率较低但是行为和要测试的函数一致, 然后针对相同的随机输入检查两者的输出结果. 第二种是生成的随机输入的数据遵循特定的模式, 这样我们就可以知道期望的输出的模式. + +下面的例子使用的是第二种方法: randomPalindrome 函数用于随机生成回文字符串. + +```Go +import "math/rand" + +// randomPalindrome returns a palindrome whose length and contents +// are derived from the pseudo-random number generator rng. +func randomPalindrome(rng *rand.Rand) string { + n := rng.Intn(25) // random length up to 24 + runes := make([]rune, n) + for i := 0; i < (n+1)/2; i++ { + r := rune(rng.Intn(0x1000)) // random rune up to '\u0999' + runes[i] = r + runes[n-1-i] = r + } + return string(runes) +} + +func TestRandomPalindromes(t *testing.T) { + // Initialize a pseudo-random number generator. + seed := time.Now().UTC().UnixNano() + t.Logf("Random seed: %d", seed) + rng := rand.New(rand.NewSource(seed)) + + + for i := 0; i < 1000; i++ { + p := randomPalindrome(rng) + if !IsPalindrome(p) { + t.Errorf("IsPalindrome(%q) = false", p) + } + } +} +``` + +虽然随机测试有不确定因素, 但是它也是至关重要的, 我们可以从失败测试的日志获取足够的信息. 在我们的例子中, 输入 IsPalindrome 的 p 参数将告诉我们真实的数据, 但是对于函数将接受更复杂的输入, 不需要保存所有的输入, 只要日志中简单地记录随机数种子即可(像上面的方式). 有了这些随机数初始化种子, 我们可以很容易修改测试代码以重现失败的随机测试. + +通过使用当前时间作为随机种子, 在整个过程中的每次运行测试命令时都将探索新的随机数据. 如果你使用的是定期运行的自动化测试集成系统, 随机测试将特别有价值. + +**练习 11.3:** TestRandomPalindromes 只测试了回文字符串. 编写新的随机测试生成器, 用于测试随机生成的非回文字符串. + +**练习 11.4:** 修改 randomPalindrome 函数, 以探索 IsPalindrome 对标点和空格的处理. + + +