doute3621 2016-04-18 08:26
浏览 88
已采纳

Golang测试模拟功能最佳实践

I am developing some tests for my code (using the testing package), and I am wondering what's the best way to mock functions inside the tested function:

Should I pass the function as parameter? In that case, what if that function calls another function? Should I pass both the first and second function as parameters in the tested one?

Note: some of the functions are called on objects (i.e. someObj.Create()) and use HTTP API calls.

UPDATE for clarification:

Example: functions

func f1() error {
  ... //some API call
}

func (s *SomeStruct) f2() error {
  return f1
}

func f3() error {
  return nil
}

func f4() error {
  ...
  err = obj.f2()
  ...
  err = f3()
  ...
}

For the above: if I want to test f4, what's the best way to mock f2 and f3?

If I pass f2 and f3 to f4 as parameters it would work, but then what for the f2 test? Should I pass f1 to f2 as parameter?

And if that's it, should then f4 have f1 as well in the parameters?

  • 写回答

2条回答 默认 最新

  • douxian1923 2016-04-18 14:28
    关注

    As a general guideline, functions aren't very mockable so its in our best interests to mock structs that implement a certain interface that may be passed into functions to test the different branches of code. See below for a basic example.

    package a
    
    type DoSomethingInterface interface {
        DoSomething() error
    }
    
    
    func DoSomething(a DoSomethingInterface) {
        if err := a.DoSomething(); err != nil {
            fmt.Println("error occurred")
            return
        }
        fmt.Println("no error occurred")
        return
    }
    
    package a_test
    
    import (
        "testing"
        "<path to a>/a"
    )
    
    type simpleMock struct {
        err error
    }
    
    func (m *simpleMock) DoSomething() error {
        return m.err
    }
    
    func TestDoSomething(t *testing.T) {
        errorMock := &simpleMock{errors.New("some error")}
        a.DoSomething(errorMock)
        // test that "an error occurred" is logged
    
        regularMock := &simpleMock{}
        a.DoSomething(regularMock)
        // test "no error occurred" is logged
    }
    

    In the above example, you would test the DoSomething function and the branches that happens eg. you would create an instance of the mock with an error for one test case and create another instance of the mock without the error to test the other case. The respective cases are to test a certain string has been logged to standard out; in this case it would be "error occurred" when simpleMock is instantiated with an error and "no error occurred" when there simpleMock is not instantiated with an error.

    This can of course be expanded to other cases eg. the DoSomething function actually returns some kind of value and you want to make an assertion on the value.

    Edit:

    I updated the code with the concern that the interface lives in another package. Note that the new updated code has a package a that contains the interface and the function under test and a package a_test that is merely a template of how to approach testing a.DoSomething.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 急matlab编程仿真二阶震荡系统
  • ¥20 TEC-9的数据通路实验
  • ¥15 ue5 .3之前好好的现在只要是激活关卡就会崩溃
  • ¥50 MATLAB实现圆柱体容器内球形颗粒堆积
  • ¥15 python如何将动态的多个子列表,拼接后进行集合的交集
  • ¥20 vitis-ai量化基于pytorch框架下的yolov5模型
  • ¥15 如何实现H5在QQ平台上的二次分享卡片效果?
  • ¥15 python爬取bilibili校园招聘网站
  • ¥30 求解达问题(有红包)
  • ¥15 请解包一个pak文件