douqiang1910
douqiang1910
2015-06-05 00:41

在go中测试不可返回的方法

已采纳

I have a simple method, which just checks if a parameter is empty and then calls one of 2 methods based on a struct field.
How would I test it?

func (cT *customType) someMethod(message string) {
    if message == ""{
        return
    }

    if cT.value == nil {
        cT.doFunctionOne()
    } else {
        cT.doFunctionTwo()
    }
}

In Javascript, I would create a spy on doFunctionOne() and mock up the object.
Mocking up works well in Go as well, but how would I do the 'spying' part?
Or is there another idiomatic way to test this kind of method?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

3条回答

  • douzhong1907 douzhong1907 6年前

    First: You wouldn't name a method "doFunctionOne" but "methodOne" :-)

    If neither doFunctionOne nor doFunctionTwo has any observable effect, then there is absolutely no point in testing it. So we may assume that they do have observable side effect, either on the environment or on the customType they have been invoked on.

    Now just test these side effects. This is trivial if both methods do return. If they spin up an endless loop it becomes harder, but still doable.

    IMHO there is no need to "test" this method in the sense of "test whether it calls One or Two depending on value": For me this is to lowlevel, too much increasing coverage count for nothing. If you call some method it has to do something (observable effect) and you should check this effect, not the inner workings.

    点赞 评论 复制链接分享
  • dpmwy80068 dpmwy80068 6年前
    1. You can add log before each function call.
    2. Inject your own logger implementation, []string for example.
    3. Check the string slice for a matching strings.

    But, if you are creating a function factory it would be better if you return the function to the caller and the caller will run the function.
    Then testing is straightforward.

    Second but, there is only two kind of functions, flow function and logic functions. You mixed flow and logic in the same function. Testing difficulty is just one symptom of this bad practice.

    点赞 评论 复制链接分享
  • duanhu2414 duanhu2414 6年前

    The idiomatic way of mocking an object in Go is to make it explicit. A healthy interface should be testable by itself. So if we have something like this:

    type customType struct {
        value int
    }
    
    func (c customType) doFunctionOne() {
        fmt.Println("Method #1")
    }
    
    func (c customType) doFunctionTwo() {
        fmt.Println("Method #2")
    }
    
    func (c customType) someMethod() {
        if c.value <= 0 {
            c.doFunctionOne()
        } else {
            c.doFunctionTwo()
        }
    }
    

    We have to provide a way to change the implementation of doFunctionOne and doFunctionTwo explicitly. We can generalize the someMethod behavior using interfaces:

    type customType struct {
        myValue int
    }
    
    func (c customType) doFunctionOne() {
        fmt.Println("Method #1")
    }
    
    func (c customType) doFunctionTwo() {
        fmt.Println("Method #2")
    }
    
    func (c customType) value() int {
        return c.myValue
    }
    
    type Interface interface {
        value() int
        doFunctionOne()
        doFunctionTwo()
    }
    
    func someMethod(i Interface) {
        if i.value() <= 0 {
            i.doFunctionOne()
        } else {
            i.doFunctionTwo()
        }
    }
    
    type customTestingType struct {
        t *testing.T
    }
    
    func (c customTestingType) doFunctionOne() {
        c.t.Log("Working")
    }
    
    func (c customTestingType) doFunctionTwo() {
        c.t.Error("Not working")
    }
    
    func (c customTestingType) value() int {
        return 0
    }
    
    func TestInterface(t *testing.T) {
        someMethod(customTestingType{t})
    }
    

    Surely there will be more ways to provide this behavior but it depends on the particular declaration of your type. As an example, you can look at httptest package. That said, if you really want to mock your type in that way (nonidiomatic), you can some unsafe monkey patching:

    package main
    
    import (
        "fmt"
        "reflect"
    
        "github.com/bouk/monkey"
    )
    
    type customType struct {
        myValue int
    }
    
    func (c customType) doFunctionOne() {
        fmt.Println("Method #1")
    }
    
    func (c customType) doFunctionTwo() {
        fmt.Println("Method #2")
    }
    
    func (c customType) someMethod() {
        if c.myValue <= 0 {
            c.doFunctionOne()
        } else {
            c.doFunctionTwo()
        }
    }
    
    func main() {
        c := customType{0}
        monkey.PatchInstanceMethod(reflect.TypeOf(c), "doFunctionOne",
            func(c customType) {
                fmt.Println("Method #1, but patched")
            })
        monkey.PatchInstanceMethod(reflect.TypeOf(c), "doFunctionTwo",
            func(c customType) {
                fmt.Println("Method #2, but patched")
            })
        c.someMethod()
    }
    
    点赞 评论 复制链接分享

为你推荐