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
。我怎样才能做到这一点?
创建一个接口并将其作为参数传递给唯一的方法吗?
答案 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()
}
我希望这会有所帮助。