一尘不染

进行单元测试致命伤并测试main()

go

我刚开始使用来自PHP背景的PHPUnit测试进行测试。

在PHP中,非常讲道,您需要100%的覆盖率。在Go中,我所读到的关于测试的大多数内容似乎很少,没有诸如挑衅之类的东西。

例如我的小程序:

func main() {
    config = readConfig("config.json")
}

func readConfig(path string) Config {
    var cfg Config
    file, err := ioutil.ReadFile(path)
    if err != nil {
        log.Fatal(err)
    }
    err = json.Unmarshal(file, &cfg)
    if err != nil {
        log.Fatal(err)
    }
    return cfg
}

func TestCanReadConfig(t *testing.T) {
    cfg := readConfig("test_data/config.json")
    if cfg.Broker_pass != "test" || cfg.Broker_port != "3333" {
        t.Error("invalid config")
    }
}

现在在我的示例中,我会遇到覆盖问题,因为在单元测试中根本没有覆盖main()(应该怎么办?)

而且两个log.Fatal()都没有涉及。

我的问题是我该如何准确地编写测试?我是否以一种不太严格的样式进行操作,而不是测试所有可能的场景?或者可以使用php中的php注释或可以
@expectedException \InvalidArgumentException
测试主要功能吗?如果不能,我可以以某种方式从覆盖率工具中忽略它吗?我应该考虑一个测试框架吗?大多数测试教程都很不错,但是非常简短,仅介绍简单的测试。


阅读 434

收藏
2020-07-02

共1个答案

一尘不染

就其本身而言,这并不是Go的事情,它取决于您的偏好,但是:

一个。不要测试main。main应该只调用已测试的代码,最好在其他软件包中。对这些软件包提供尽可能多的代码覆盖率,并尽可能使main保持琐碎。无论覆盖范围如何,这都是一个好习惯。因此,这并不是真正的问题。

b。不要log.Fatal用于可测试的代码,只需返回错误。您可以保留log.Fatal应用程序初始化代码,即main-in
:)。因此,如果main调用readConfig失败了,它只会返回一个错误(非常容易测试!)。应用程序的log.Fatal主要行为是main的工作-
配置读取器不应处理诸如确定是否应退出应用程序之类的事情,对吗?它只是读取配置,并告诉您是否成功。应用程序决定如何处理它。

因此您的代码可能如下所示:

func readConfig(path string) (Config, error) {
    var cfg Config
    file, err := ioutil.ReadFile(path)
    if err != nil {
        return cfg, err
    }
    err = json.Unmarshal(file, &cfg)
    if err != nil {
        return cfg, err
    }
    return cfg, nil
}

func main() {
    config, err := readConfig("config.json")
    if err != nil {
        log.Fatal(err)
    }

}

现在,您已将逻辑与应用程序行为分开,并且readConfig可以完全测试。

2020-07-02