如何测试此功能的错误条件?

时间:2016-07-01 03:22:43

标签: go

func getHostname() (string, error) {
    host, err := os.Hostname()
    if err != nil {
        // Untested code-block
        return "", err
    }
    // Don't judge the example. This is just an example. 
    // Basically, I want to know how to induce error in os.Hostname()
    return host, nil

}

此功能的相应测试是: -

import "testing"

func TestGetHostname(t *testing.T) {
        host, err := getHostname()
        if err != nil {
                t.Errorf("Error executing getHostname(): %s", err)
        }
        if len(host) < 1 {
                t.Errorf("Hostname returned is not proper")
        }
}

如果我要对此功能进行100%覆盖,我想在os.Hostname()中引发错误,以便我也可以运行if block。我怎样才能做到这一点?

创建一个接口并将其作为参数传递给唯一的方法吗?

2 个答案:

答案 0 :(得分:4)

您可以使用依赖注入来使代码可测试:

// Override for testing
var osHostname = os.Hostname

func getHostname() (string, error) {
    host, err := osHostname()
    if err != nil {
        return "", err
    }
    return host, nil    
}

在测试中,您可以使用生成错误的版本替换存根。

func TestGetHostnameFails(t *testing.T) {
    defer func() { osHostname = os.Hostname }()
    osHostname = func()(string, error) { return "", errors.New("fail") }
    got, err := getHostname()
    if err == nil {
        t.Errorf("getHostname() = (%v, nil), want error", got)
    }
}

使用包全局变量进行依赖注入有其优点和缺点。优点是它非常简单,对我来说最重要的是不会过多地混淆生产代码。出于这些原因,当您想要像这样单独测试代码时,我会选择它。缺点是您可能忘记重置测试中的状态,并且它没有提供良好的测试API - 因此,如果有大量测试使用此存根(或者更糟糕的是,测试中)另一个包),您可能更喜欢将配置放在struct中,并使getHostname成为该结构的方法。

这只是一个例子,但我觉得有必要提供关于获得100%测试覆盖率的警示信息。 os.Hostname()在实践中不太可能失败,并且在失败时处理错误也不太可能。任何依赖注入引入错误的可能性都大于此测试识别任何真正的错误。

答案 1 :(得分:1)

如果您的测试功能发生恐慌,会发生什么? 为您的测试代码添加恐慌检查:

package hostname

import "testing"

func TestGetHostname(t *testing.T) {
    defer func() {
        if r := recover(); r != nil {
            t.Errorf("The code did panic")
        }
    }()
    name, err := getHostname()
    if err != nil {
        t.Errorf("err: %v\n", err)
    }
    if len(name) == 0 {
        t.Errorf("Hostname is empty")
    }
}
老了:

查看os.hostname()内部:
如果是错误,它将返回:

        return "", NewSyscallError("...", err) 

你的getHostname()再次重复这一点 让我再做一次(只是为了澄清我正在谈论的内容):

func getHostname2() (string, error) {
    host, err := getHostname()  // your getHostname !
    if err != nil {
        return "", err
    }
    return host, nil    
}

这是多余的,不是吗?

所以我认为这已经足够了:

package main

import "os"
import "fmt"

func main() {
    name, err := os.Hostname()
    if err != nil {
        fmt.Printf("err: %v\n", err)
        return
    }
    fmt.Println(name)
}

如此简单的错误检查就足够了(就像这样):

package main

import "os"
import "fmt"

func work() {
    if name, err := os.Hostname(); err != nil {
        fmt.Printf("err: %v\n", err)
        return
    } else {
        // do some job ...
        fmt.Println(name)
    }
}
func main() {
    work()
}

我希望这会有所帮助。