扩展Gotest以进行严格的错误测试


?his is my first post on dev.to, X-posted from my new personal blog which can be found here. Hopefully I'll have dev.to publish from RSS
feeds once I've worked it out!

很高兴收到您的任何反馈!??


Strict Error Tests in Java

我喜欢通过编写测试和练习测试驱动的开发(TDD)来确认代码的稳定性。 对于Java,JUnit是我首选的测试框架。 在编写测试以确认已引发异常时,我使用了可选参数预期用于注释@测试,但是我很快发现,该解决方案不适用于针对不同的错误消息多次测试相同错误类并对其进行测试的方法。

通常在编写一种验证方法(例如下面的验证方法)时可以找到该方法,该方法将采用狗的名字,如果有效则返回布尔值。

1
2
3
4
5
6
7
8
9
10
11
12
13
<wyn>public static boolean validateDogName(String dogName) throws DogValidationException {

    if (containsSymbols(dogName)) {
        throw new DogValidationException("Dogs cannot have symbols in their name!");
    }

    if (dogName.length > 100) {
        throw new DogValidationException("Who has a name for a dog that long?!");
    }

    return true;
}
</wyn>

对于这种方法,只需使用@Test(expected = DogValidationException.class)我们的测试方法还不够; 我们如何确定该异常是由于dogName.length违规而不是由于包含符号而引发的?

In order for me to resolve this, I came across the ExpectedException class for JUnit on Baeldung which enables us to specify the error message expected. Here it is applied to the test case for this method:

1
2
3
4
5
6
7
8
9
10
<wyn>@Rule
public ExpectedException exceptionRule = ExpectedException.none();

@Test
public void shouldHandleDogNameWithSymbols() {
    exceptionRule.expect(DogValidationException.class);
    exceptionRule.expectMessage("Dogs cannot have symbols in their name!");
    validateDogName("GoodestBoy#1");
}
</wyn>

Applying to Golang

Back to Golang, there is a built-in library aptly named testing which enables us to assert on test conditions. When combined with Gotests - a tool for generating Go tests from your code - writing tests could not be easier! I love how this is bundled in with the Go extension for VSCode, my text editor of choice (for now...).

转换上述JavavalidateDogNameGolang的方法将产生类似以下内容:

1
2
3
4
5
6
7
8
9
10
11
func validateDogName(name string) (bool, error) {
    if containsSymbols(name) {
        return false, errors.New("dog cannot have symbols in their name")
    }

    if len(name) > 100 {
        return false, errors.New("who has a name for a dog that long")
    }

    return true, nil
}

如果您有Go方法可传回错误接口,然后getestest将生成如下所示的测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func Test_validateDogName(t *testing.T) {
    type args struct {
        name string
    }
    tests := []struct {
        name    string
        args    args
        want    bool
        wantErr bool
    }{
        name: "Test error was thrown for dog name with symbols",
        args: args{
            name: "GoodestBoy#1",
        },
        want: false,
        wantErr: true,
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := validateDogName(tt.args.name)
            if (err != nil) != tt.wantErr {
                t.Errorf("validateDogName() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("validateDogName() = %v, want %v", got, tt.want)
            }
        })
    }
}

从上面的内容,我们仅限于可以断言的错误,这里任何返回的错误将通过测试。 这相当于使用@Test(expected = Exception.class)在JUnit中! 但是还有另一种方式

Modifying the Generated Test

我们只需要对生成的测试进行一些简单的更改即可使我们能够根据测试错误消息进行断言...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<wyn>func Test_validateDogName(t *testing.T) {
    type args struct {
        name string
    }
    tests := []struct {
        name    string
        args    args
        want    bool
        wantErr error
    }{
        name: "Test error was thrown for dog name with symbols",
        args: args{
            name: "GoodestBoy#1",
        },
        want: false,
        wantErr: errors.New("dog cannot have symbols in their name"),
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := validateDogName(tt.args.name)
            if tt.wantErr != nil && !reflect.DeepEqual(err, tt.wantErr) {
                t.Errorf("validateDogName() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("validateDogName() = %v, want %v", got, tt.want)
            }
        })
    }
}
</wyn>

上面有三处更改,让我们逐一进行研究:

  1. wantErr错误 我们正在从改变布尔这样我们就可以与函数返回的错误进行比较wantErr:errors.New(“狗的名字中不能有符号”),这是我们期望的错误结构if tt.wantErr != nil && !reflect.DeepEqual(err, tt.wantErr) {检查以确保测试预期会有错误,如果是,则将其与返回的错误进行比较

如果存在一个预期没有错误的测试用例,第3点将提供额外的支持。 注意如何wantErr在下面的测试案例中完全省略了。

1
2
3
4
5
6
7
{
    name: "Should return true for valid dog name",
    args: args{
        name: "Benedict Cumberland the Sausage Dog",
    },
    want: true,
}

Customising Gotests Generated Test

Gotests使我们能够提供自己的模板来生成测试,并且可以轻松地集成到您选择的文本编辑器中。 我将向您展示如何在VSCode中完成此操作。

  1. Check out gotests and copy the templates directory to a place of your choosing

    • git clone https://github.com/cweill/gotests.git
    • cp -R gotests/internal/render/templates ~/scratch/gotests
  2. Overwrite the contents of function.tmpl with the contents of this Gist

  3. Add the following setting to VSCode's settings.json

    • "go.generateTestsFlags": ["--template_dir=~/scratch/templates"]

完成此操作后,现在将使用更严格的错误测试来生成将来的测试! ??

Closing

我了解上述建议将使您的代码更加脆弱,因为该代码会受到下游库错误消息的任何更改的影响。 但是,对我自己而言,我更喜欢编写严格的测试,并尽量减少其他错误污染测试的机会。

我也知道,GoodestBoy#1可能是狗的有效名字! ??

from: https://dev.to//jdheyburn/extending-gotests-for-strict-error-tests-4j96