使os.Stat引发错误,以使IsNotExist返回false

时间:2018-10-24 19:36:01

标签: unit-testing go

我正在尝试测试以下分支:

if _, err := os.Stat(path); err != nil {
        if os.IsNotExist(err) {
            continue
        }
        return errors.File().AddDetails(err)
    }

很显然,如果os.Stat不存在,path将引发错误。 Reading the Golang documentation不返回有关os.Stat可能返回的错误的详细信息。有办法让os.Stat引发另一种错误吗?

2 个答案:

答案 0 :(得分:2)

您可能会导致错误,例如通过传递无效的文件名 IsNotExist 返回 false。

package main

import (
    "fmt"
    "os"
)

func main() {
    _, err := os.Stat("\000x")
    fmt.Println(err)

    // prints:
    // 
    // stat x: invalid argument
}

\000 (ASCII: NUL) 是 unix 文件名中的无效字符。

答案 1 :(得分:1)

我认为您会发现很难os.Stat以与单元测试无关的平台方式控制抛出什么错误以及何时发生错误。如果您确实需要测试返回未知错误类型的路径,那么最好的选择是重构软件包代码,以便模拟os.Stat。尽管您不能直接更改os.Stat的行为,但是通过利用Go具有一流的功能这一事实,您可以使用一些间接的方式来对它进行模拟,而对代码的更改却很少。如果您的软件包代码如下所示:

package mypackage

import "os"

...

    if _, err := os.Stat(path); err != nil {
        if os.IsNotExist(err) {
           continue
        }
        return errors.File().AddDetails(err)
    }

...

尝试对其进行重构,以使用分配给os.Stat的未导出的软件包作用域函数变量:

package mypackage

import "os"

var osStat = os.Stat

...

    if _, err := osStat(path); err != nil {
        if os.IsNotExist(err) {
           continue
        }
        return errors.File().AddDetails(err)
    }

...

现在,在测试代码中(应该与被测试的代码位于相同的程序包中),您可以将osStat重新分配给具有相同签名的任何函数以对其进行模拟:

package mypackage

import (
        "os"
        "testing"
)

func TestNotExistError(t *testing.T) {
    osStat = func(string) (os.FileInfo, error) {
        return nil, os.ErrNotExist
    }
    // in case other test functions depend on the unmocked behavior
    defer func() {
        osStat = os.Stat
    }()

    // rest of the test which triggers the codepath above
}

func TestOtherError(t *testing.T) {
    osStat = func(string) (os.FileInfo, error) {
        return nil, os.ErrInvalid
    }
    // in case other test functions depend on the unmocked behavior
    defer func() {
        osStat = os.Stat
    }()

    // rest of the test which triggers the codepath above
}

...