译 | Prefer table driven tests(二)

简介: 译 | Prefer table driven tests(二)

Enumerating test cases

由于测试存储在 slice 中,我们可以在失败消息中打印出测试用例的索引:

func TestSplit(t *testing.T) {  
    tests := []struct {  
        input string  
        sep . string  
        want  []string  
    }{  
        {input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},  
        {input: "a/b/c", sep: ",", want: []string{"a/b/c"}},  
        {input: "abc", sep: "/", want: []string{"abc"}},  
        {input: "a/b/c/", sep: "/", want: []string{"a", "b", "c"}},  
    }  
    for i, tc := range tests {  
        got := Split(tc.input, tc.sep)  
        if !reflect.DeepEqual(tc.want, got) {  
            t.Fatalf("**test %d:** expected: %v, got: %v", **i+1**, tc.want, got)  
        }  
    }  
}

现在,当我们运行 go test 我们得到了这个:

% go test  
--- FAIL: TestSplit (0.00s)  
    split_test.go:24: **test 4:** expected: [a b c], got: [a b c ]

这样好了一些。 现在我们知道第四个测试失败了,尽管我们不得不做了一点点捏造,因为 slice 索引和范围迭代是从 0 开始的。 这要求您的测试用例保持一致; 如果有些人从 0 开始报告而其他人使用 1 开始报告,那将会令人困惑。 并且,如果测试用例列表很长,则可能很难数大括号以确切地确定第4个测试用例由哪些结构构成。

Give your test cases names

另一种常见模式是在测试结构中包含名称字段。

func TestSplit(t *testing.T) {  
    tests := []struct {  
        **name  string**  
        input string  
        sep   string  
        want  []string  
    }{  
        {name: "simple", input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},  
        {name: "wrong sep", input: "a/b/c", sep: ",", want: []string{"a/b/c"}},  
        {name: "no sep", input: "abc", sep: "/", want: []string{"abc"}},  
        {name: "trailing sep", input: "a/b/c/", sep: "/", want: []string{"a", "b", "c"}},  
    }  
    for _, tc := range tests {  
        got := Split(tc.input, tc.sep)  
        if !reflect.DeepEqual(tc.want, got) {  
            t.Fatalf("**%s:** expected: %v, got: %v", **tc.name**, tc.want, got)  
        }  
    }  
}

现在,当测试失败时,我们有一个描述性的名称,描述正在进行的测试。 我们不再需要尝试从输出中找出它 —— 现在还有一个字符串,我们可以搜索。

% go test 
--- FAIL: TestSplit (0.00s)  
    split_test.go:25: **trailing sep**: expected: [a b c], got: [a b c ]

我们可以使用 map 字面值语法来更详细地说明这一点:

func TestSplit(t *testing.T) {  
    tests := **map[string]struct {  
        input string  
        sep   string  
        want  []string  
    }**{   
        "simple":       {input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},   
        "wrong sep":    {input: "a/b/c", sep: ",", want: []string{"a/b/c"}},  
        "no sep":       {input: "abc", sep: "/", want: []string{"abc"}},  
        "trailing sep": {input: "a/b/c/", sep: "/", want: []string{"a", "b", "c"}},  
    }  
    for name, tc := range tests {  
        got := Split(tc.input, tc.sep)  
        if !reflect.DeepEqual(tc.want, got) {  
            t.Fatalf("**%s:** expected: %v, got: %v", **name**, tc.want, got)  
        }  
    }  
}

使用 map 字面值语法,我们不再将测试用例定义为结构的 slice,而是作为测试名到测试结构的 map。 使用可能会提高测试效果的 map 还有一个好处。

map 迭代顺序是 undefined1 这意味着每次运行 go test,我们的测试都可能以不同的顺序运行。

这对于发现在按语句顺序运行时测试通过的条件非常有用,但不适用于其他情况。如果您发现这种情况发生了,您可能是有一些全局状态,被一次测试改变,而后续测试取决于该修改。

Introducing sub tests

在我们修复失败的测试之前,还有一些其他问题需要在我们的 table driven test 工具中解决。

第一,我们在其中一个测试用例失败时调用t.Fatalf。 这意味着在第一次失败的测试用例之后我们停止测试其他情况。 因为测试用例是以未定义的顺序运行的,所以如果测试失败,那么知道它是唯一的失败还是只是第一次失败会更好。

如果我们努力将每个测试用例写出来作为测试包的函数,测试包将为我们做到这一点,但是这很冗长。 好消息是,自从Go 1.7添加了一项新功能,让我们可以轻松地进行 table driven test。 它们被称为 sub tests

func TestSplit(t *testing.T) {  
    tests := map[string]struct {  
        input string  
        sep   string  
        want  []string  
    }{  
        "simple":       {input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},  
        "wrong sep":    {input: "a/b/c", sep: ",", want: []string{"a/b/c"}},  
        "no sep":       {input: "abc", sep: "/", want: []string{"abc"}},  
        "trailing sep": {input: "a/b/c/", sep: "/", want: []string{"a", "b", "c"}},  
    }  
    for name, tc := range tests {  
**t.Run(name, func(t *testing.T) {  
            got := Split(tc.input, tc.sep)  
            if !reflect.DeepEqual(tc.want, got) {  
                t.Fatalf("expected: %v, got: %v", tc.want, got)  
            }  
        })**  
    }  
}

由于每个 sub test 现在都有一个名称,我们可以在任何测试运行中自动打印出该名称。

% go test 
--- FAIL: TestSplit (0.00s)  
    --- FAIL: **TestSplit/trailing_sep** (0.00s)  
        split_test.go:25: expected: [a b c], got: [a b c ]

每个 subtest 都是它自己的匿名函数,因此我们可以使用 t.Fatalft.Skipf 和所有其他 testing.T helper,同时保留table driven test 的紧凑性。


目录
相关文章
|
8天前
|
人工智能 机器人 测试技术
【CMake报错】报错:Flow control statements are not properly nested.
【CMake报错】报错:Flow control statements are not properly nested.
|
8天前
|
存储 SQL API
TinyKv Project4 Transactions
TinyKv Project4 Transactions
74 0
|
10月前
|
测试技术 Shell Go
译 | Prefer table driven tests(一)
译 | Prefer table driven tests
54 0
|
10月前
|
缓存 测试技术 Go
译 | Prefer table driven tests(三)
译 | Prefer table driven tests(三)
60 0
|
Java 测试技术
出现Error creating bean with name与CONDITIONS EVALUATION REPORT问题
出现Error creating bean with name与CONDITIONS EVALUATION REPORT问题
305 0
出现Error creating bean with name与CONDITIONS EVALUATION REPORT问题
Important table for launchpad Designer and configuration
Important table for launchpad Designer and configuration
109 0
Important table for launchpad Designer and configuration
SAP Fiori application do filtering will real delete note in DB
SAP Fiori application do filtering will real delete note in DB
103 0
SAP Fiori application do filtering will real delete note in DB
Important table for Fiori launchpad Designer and configuration
Important table for Fiori launchpad Designer and configuration
101 0
Important table for Fiori launchpad Designer and configuration
UDO report generate DDIC table
UDO report generate DDIC table
121 0
UDO report generate DDIC table
Action framework BAdI Definition TRIGGER_EXECUTED
Created by Jerry Wang, last modified on May 28, 2014
148 0
Action framework BAdI Definition TRIGGER_EXECUTED

热门文章

最新文章