doulin4844 2019-06-01 02:13
浏览 25

用结构内部的方法测试错误响应

I'm writing unit tests, and I want to write a unit test that asserts that a public method on the struct (Foo.Start) properly handles error responses from an internal method on the struct (Foo.internal).

Essentially I want to get test coverage on the this section of my code:

if err != nil {
    return err
}

Here's an example of code and associated test that doesn't work (but would work in say, Python)

# example.go

package example

import "fmt"

type FooAPI interface {
    Start() error
    internal(string) (string, error)
}

type Foo struct {
    FooAPI
}

func (foo Foo) Start() (err error) {

    data, err := foo.internal("bar")
    if err != nil {
        return err
    }

    fmt.Println(data)
    return err
}

func (foo Foo) internal(input string) (output string, err error) {
    return output, err
}
# example_test.go

package example

import (
    "testing"

    "github.com/pkg/errors"
)

type MockFoo struct {
    FooAPI
}

func (foo MockFoo) internal(input string) (output string, err error) {
    return output, errors.New("big error")
}

func TestStart(t *testing.T) {
    tdata := []struct {
        testCase        string
        expectedAnError bool
        foo             FooAPI
    }{
        {
            testCase:        "standard_case",
            expectedAnError: false,
            foo:             Foo{},
        },
        {
            testCase:        "error_case",
            expectedAnError: true,
            foo:             MockFoo{},
        },
    }
    for _, test := range tdata {
        t.Run(test.testCase, func(t *testing.T) {
            // The function under test
            test.foo.Start = Foo.Start // <= this doesn't work
            err := test.foo.Start()

            // Assertion 1
            if test.expectedAnError == false && err != nil {
                t.Error(err.Error())
            }

            // Assertion 2
            if test.expectedAnError == true && err == nil {
                t.Error("expected an error, but there was none!")
            }
        })
    }
}

I'm less interested in correcting the specific example, more-so my goal is to get test coverage on Foo.Start's error handling. I feel like there's some trick with interfaces or pointers that will get me across the finish line here?

  • 写回答

1条回答 默认 最新

  • dougu2036 2019-06-01 04:53
    关注

    I figured out one solution, inspired by https://stackoverflow.com/a/48206430/3055558

    Using a internal{{ your struct }} struct and associated interface, and mocking that.

    # example.go
    
    package example
    
    import "fmt"
    
    type internalFooAPI interface {
        internalBehavior(string) (string, error)
    }
    
    type Foo struct {
        internal internalFooAPI
    }
    
    type internalFoo struct{}
    
    func NewFoo(internal internalFooAPI) Foo {
        return Foo{
            internal: internal,
        }
    }
    
    func (foo Foo) Start() (err error) {
    
        data, err := foo.internal.internalBehavior("bar")
        if err != nil {
            return err
        }
    
        fmt.Println(data)
        return err
    }
    
    func (foo internalFoo) internalBehavior(input string) (output string, err error) {
        return output, err
    }
    
    # example_test.go
    
    package example
    
    import (
        "testing"
    
        "github.com/pkg/errors"
    )
    
    type mockInternalFoo struct{}
    
    type mockInternalFooWithErrors struct{}
    
    func (foo mockInternalFoo) internalBehavior(input string) (output string, err error) {
        return output, err
    }
    
    func (foo mockInternalFooWithErrors) internalBehavior(input string) (output string, err error) {
        return output, errors.New("big error")
    }
    
    func TestStart(t *testing.T) {
        tdata := []struct {
            testCase        string
            expectedAnError bool
            foo             Foo
        }{
            {
                testCase:        "standard_case",
                expectedAnError: false,
                foo:             NewFoo(internalFoo{}),
            },
            {
                testCase:        "mock_case",
                expectedAnError: false,
                foo:             NewFoo(mockInternalFoo{}),
            },
            {
                testCase:        "error_case",
                expectedAnError: true,
                foo:             NewFoo(mockInternalFooWithErrors{}),
            },
        }
        for _, test := range tdata {
            t.Run(test.testCase, func(t *testing.T) {
                // The function under test
                err := test.foo.Start()
    
                // Assertion 1
                if test.expectedAnError == false && err != nil {
                    t.Error(err.Error())
                }
    
                // Assertion 2
                if test.expectedAnError == true && err == nil {
                    t.Error("expected an error, but there was none!")
                }
            })
        }
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测