繁体   English   中英

Mocking go 测试中结构的方法调用

[英]Mocking a struct's method call in go tests

我正在寻找在测试中模拟结构的方法以提高代码覆盖率。 有一些关于这个的帖子,没有一个对我有用。 我可能完全弄错了。

main/file1.go

type application struct {
    Name string
 }

func (app *application) find() error {
    // perform function logic
    return nil
}

func main() {
    app := &application{
        Name:   "Main Application",
    }

    err := app.find()
    if err != nil {
        fmt.Printf("Error in find call: %s\n", err)
    }
}

我需要能够模拟find()的测试并返回一个error (我不想生成一个可能导致错误的测试用例,因为这不在我的控制之下,我不知道如何通过传递可接受的参数)。 我试图遵循这篇文章的第二个答案,但编译器不喜欢它。

main/file1_test.go

func Test_application_find(t *testing.T) {
    tests := []struct {
        name           string
        app            *application
        wantErr        string
    }{
        {
            name: "generate error",
            app: &application{
                Name: "Mock Application",
            },
            wantErr: true,
        },
    }
    for _, tt := range tests {
        mockCaller := tt.app.find // this works fine
        tt.app.find = func() error { // this assignment errors out
            return fmt.Errorf("Mock Error Message")
        }
        defer func() {
            tt.app.find = mockCaller // this assignment errors out
        }()

        t.Run(tt.name, func(t *testing.T) {
            if err := tt.app.find(); (err != nil) && (err.Error() != "Mock Error Message") {
                    t.Errorf("error = %s, wantErr %s", err.Error(), tt.wantErr)
            }
        } 
    }
}

我在这里做错了什么? 请建议。

有几种方法可以模拟一个方法。 他们都有适合的情况。

作品

对于测试,您在原始结构之上创建一个新结构。

struct testApp struct{
    application
}

现在teststruct的行为方式几乎与app相同,但您可以覆盖 find function:

func (app *testApp) find() error {
    return errors.New("some error")
}

在测试中,您使用给定的适当app初始化testApp

app := testApp{
    application: &application{
        Name: "Mock Application",
    }
}

现在您可以在app变量上编写测试。

请注意,这不适用于所有用例,因为组合不是 inheritance。 在原始application上调用 function 不会在 testApp 中调用新的find testApp

界面

您还可以模拟整个应用程序结构/测试所需的部分。 如果您想测试另一个依赖于application的结构,这主要是有意义的。

要测试的结构不应该直接使用application ,而是它需要的功能的接口。 (这可能意味着重构你的代码来做到这一点。)

然后使用模拟的find function 创建一个模拟并将模拟传递给要测试的结构:

type app interface{
    find() error
}

type mockApp struct{
    Name string
}

(s *mockApp) find() error {
    return errors.New("some error")
}

结构字段

您还可以将find function 设为结构字段。 然后您可以覆盖它以进行测试。 然而,这具有必须更改代码的缺点。

type application struct {
    Name string
    find func() error
}

编写一个创建者,以便能够以合理的方式初始化结构:

func newApplication() *application {
    return &application{
        Name: "Main Application",
        find: func() error {
            // perform function logic
            return nil
        }
    }
}

现在您可以在测试中覆盖find方法(实际上它现在是一个字段):

app := newApplication()
app.find = func() error {
    return errors.New("some error")
}

暂无
暂无

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

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