繁体   English   中英

用testify模拟表驱动测试

[英]Table driven tests with testify mock

是否有使用testify编写干净的表驱动测试的示例。 输入和预期输出的表驱动测试运行良好,但必须测试依赖项的输出似乎真的很难。

下面的示例使用一个模拟接口,并要求我编写一个全新的测试函数来验证测试中的函数是否正确处理依赖性错误。 我只是在寻找建议,使用testify模拟软件包进行更简化的编写单元测试。

package packageone

import (
    "errors"
    "musings/packageone/mocks"
    "testing"
)
//Regular Table driven test
func TestTstruct_DoSomething(t *testing.T) {
    testObj := new(mocks.Dinterface)

    passes := []struct {
        Input  int
        Output int
    }{{0, 0}, {1, 1}, {2, 4}, {100, 10000}}

    for _, i := range passes {
        testObj.On("DoSomethingWithD", i.Input).Return(i.Output, nil)
    }

    type fields struct {
        DC Dinterface
    }
    type args struct {
        i int
    }
    tests := []struct {
        name    string
        fields  fields
        args    args
        wantRes int
        wantErr bool
    }{
        {"Pass#0", fields{testObj}, args{passes[0].Input}, passes[0].Output, false},
        {"Pass#1", fields{testObj}, args{passes[1].Input}, passes[1].Output, false},
        {"Pass#2", fields{testObj}, args{passes[2].Input}, passes[2].Output, false},
        {"Pass#3", fields{testObj}, args{passes[3].Input}, passes[3].Output, false},
        {"Fail#4", fields{testObj}, args{-1}, 0, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            r := &Tstruct{
                DC: tt.fields.DC,
            }
            gotRes, err := r.DoSomething(tt.args.i)
            if (err != nil) != tt.wantErr {
                t.Errorf("Tstruct.DoSomething() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if gotRes != tt.wantRes {
                t.Errorf("Tstruct.DoSomething() = %v, want %v", gotRes, tt.wantRes)
            }
        })
    }
}

//Separate Unit test for dependency returning errors.
func TestTstruct_ErrMock_DoSomething(t *testing.T) {
    testObj := new(mocks.Dinterface)
    testObj.On("DoSomethingWithD", 1).Return(0, errors.New(""))

    type fields struct {
        DC Dinterface
    }
    type args struct {
        i int
    }
    tests := []struct {
        name    string
        fields  fields
        args    args
        wantRes int
        wantErr bool
    }{
        {"Test#1", fields{testObj}, args{1}, 0, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            r := &Tstruct{
                DC: tt.fields.DC,
            }
            gotRes, err := r.DoSomething(tt.args.i)
            if (err != nil) != tt.wantErr {
                t.Errorf("Tstruct.DoSomething() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if gotRes != tt.wantRes {
                t.Errorf("Tstruct.DoSomething() = %v, want %v", gotRes, tt.wantRes)
            }
        })
    }
}

编写单元测试相对容易。 编写好的单元测试很难 这没有帮助,因为我们将介绍使用不能模仿现实生活用途的简单代码示例进行单元测试。

除非您需要验证依赖项的调用,否则请尽量避免模拟。 喜欢使用存根,假货或实际实现。 知道何时使用每个都是经验问题和难点所在。另外,考虑一下你的设计。 如果您发现难以进行单元测试,这可能是因为您需要重新设计。

单元测试需要时间来编写和维护。 无需单元测试,您将始终更快地编写代码。 但是,我们编写单元测试以确保我们的代码能够正常运行并且可以重新考虑因素。

因此,尝试针对行为(黑盒子)而不是实现(白盒子)编写测试是很重要的。 这并不总是可行的,但与实现相关的单元测试是脆弱的,不鼓励重构,有时也可能掩盖意外行为。

一些单元测试资源值得一读:

  1. 模拟不是存根
  2. 在厕所博客上测试
  3. TDD - 一切都出错了

作为一个例子,考虑为简单的电子邮件地址验证器编写单元测试。 我们想要编写一个函数,它将获取一个字符串并根据是否提供了有效的电子邮件地址返回true / false。

一个简单的示例实现将是:

var re = regexp.MustCompile("[regular expression]")
func ValidateEmail(s string) bool {
   return re.MatchString(s)
}

然后我们将使用各种输入编写表驱动测试,例如""good@example.combad等,并验证结果是否正确。

现在这是一个微不足道的例子,但说明了我的观点。 有人可能会说这很容易,因为函数没有依赖关系,但确实如此! 我们依赖于regexp实现和我们传递的正则表达式。

这是测试期望的行为,而不是我们如何实现它。 我们不关心它如何验证电子邮件地址,只是它确实如此。 如果我们要调整正则表达式或完全改变实现,那么除非结果不正确,否则这些都不会破坏测试。

很少有人会建议我们应该通过模拟正则表达式并确保使用我们期望的正则表达式调用它来隔离依赖关系并测试验证函数。 这将更加脆弱,但也没那么有用,即我们怎么知道正则表达式实际上会起作用?


对于您的具体示例,您可以轻松避免模拟和使用琐碎的假冒来测试正常结果和错误情况。 这将是这样的:

// Used to test error result, 
var errFail = errors.New("Failed")

// Fake type
type fakeD func(input int) (int, error)

// Implements Dinterface
func (f fakeD) DoSomethingWithD(input int) (int, error) {
    return f(input)
}

// Fake implementation. Returns error on input 5, otherwise input * input
var fake fakeD = func(input int) (int, error) {
    if input == 5 {
        return nil, errFail
    }
    return input * input, nil
}

然后简单地使用fake作为您的依赖项并正常运行基于表的测试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM