【问题标题】:testing non returning method in go在 go 中测试非返回方法
【发布时间】:2015-06-05 00:41:02
【问题描述】:

我有一个简单的方法,它只检查参数是否为空,然后根据结构字段调用 2 个方法之一。
我将如何测试它?

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

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

在 Javascript 中,我会在 doFunctionOne() 上创建一个间谍并模拟该对象。
模拟在Go 中也很有效,但我将如何执行“间谍”部分?
还是有另一种惯用的方法来测试这种方法?

【问题讨论】:

  • 按原样,您的方法没有意义:您没有将参数用于任何事情,并且检查不相关。让我觉得它应该是一个函数……

标签: testing go


【解决方案1】:

首先:您不会将方法命名为“doFunctionOne”,而是“methodOne”:-)

如果 doFunctionOne 和 doFunctionTwo 都没有任何可观察到的效果,那么测试它绝对没有意义。因此,我们可以假设它们确实具有可观察到的副作用,无论是对环境还是在调用它们的 customType 上。

现在只需测试这些副作用。如果两种方法都返回,这是微不足道的。如果他们旋转一个无限循环,它会变得更难,但仍然可行。

恕我直言,在“根据值测试它是否调用一或二”的意义上,没有必要“测试”此方法:对我来说,这是低级的,太多的增加覆盖率毫无意义。如果你调用一些方法,它必须做一些事情(可观察的效果),你应该检查这个效果,而不是内部工作。

【讨论】:

  • 你是对的。我还是个测试新手,也许这种方法不是一个很好的例子。我想问一个通用的方法来做这种“间谍”。因为我经常有这些方法,它们只是不返​​回,只调用另一个方法。例如,侦听通道的方法,处理接收到的数据并以该数据作为参数调用另一个方法。
【解决方案2】:

在 Go 中模拟对象的惯用方式是使其显式化。一个健康的接口应该是可以自己测试的。所以如果我们有这样的事情:

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()
    }
}

我们必须提供一种方法来显式更改doFunctionOnedoFunctionTwo 的实现。我们可以使用接口概括someMethod 的行为:

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})
}

当然会有更多的方法来提供这种行为,但这取决于你的类型的特定声明。例如,您可以查看httptest 包。也就是说,如果您真的想以这种方式模拟您的类型(非惯用语),您可以使用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()
}

【讨论】:

    【解决方案3】:
    1. 您可以在每个函数调用之前添加日志。
    2. 注入您自己的记录器实现,例如 []string。
    3. 检查字符串切片中是否有匹配的字符串。

    但是,如果您正在创建一个函数工厂,最好将函数返回给调用者并且调用者将运行该函数。
    然后测试很简单。

    其次,只有两种功能,流程功能和逻辑功能。 您在同一个函数中混合了流程和逻辑。 测试困难只是这种不良做法的一个症状。

    【讨论】:

      猜你喜欢
      • 2018-11-05
      • 2014-11-08
      • 1970-01-01
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多