【问题标题】:How to test file.Open with os.O_RDWR and os.O_CREATE flags?如何测试文件。使用 os.O_RDWR 和 os.O_CREATE 标志打开?
【发布时间】:2019-08-21 08:49:37
【问题描述】:

我正在尝试为 struct 构造函数编写单元测试,如果在 file.Open 期间发生错误,它也可能返回 nil。我不知道如何使用标志测试/模拟文件错误:os.O_RDWR|os.O_CREATE|os.O_APPEND

我尝试在 test 中检查 nil 值,但失败了。

构造函数:

type App struct {
    someField string
    log *log.Logger
}

func New() *App {
    app := &App{}
    f, err := os.OpenFile("info.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("error opening file: %v", err)
        return nil
    }
    mw := io.MultiWriter(os.Stdout, f)
    l = log.New(mw, "APP", log.Ldate|log.LstdFlags|log.Lshortfile)
    app.log = l

    return app
}

并测试构造函数:

func TestNew(t *testing.T) {
    var a App
    a = New()

    // doesn't cover
    if a == nil {
        t.Fatal("Error opening file")
    }
}

我希望覆盖错误!= nil,覆盖范围是红色:

f, err := os.OpenFile("info.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
  fmt.Printf("error opening file: %v", err)
  return nil
}

【问题讨论】:

  • 有些东西不适合单元测试。如果您在需要对文件系统错误进行单元测试的环境中工作:询问您环境中的某人您的测试套件如何注入,例如磁盘已满错误。
  • 发布的代码无法编译。见play.golang.org/p/BSumX2EKASJ

标签: unit-testing go


【解决方案1】:

Go 中的 Mocking 意味着拥有接口,如果你真的需要这些接口,你可以考虑使用类似 https://github.com/spf13/afero 的东西,而不是直接使用 os 包。这还允许您使用内存中的文件系统和其他使测试更容易的东西。

【讨论】:

    【解决方案2】:

    有两件事需要考虑。

    第一个是O_RDWR|O_CREAT|O_APPEND 在打开文件时几乎没什么意思:它告诉操作系统它应该以附加模式打开文件进行读写,并且如果文件不存在于应该创建调用的时间,否则可以附加到它。

    现在我可以理解此操作可能失败的唯一两个原因是:

    • 包含文件的文件系统以只读方式挂载 - 因此打开文件进行写入、创建和附加是不可能的。
    • 文件不存在且文件系统的 inode 表已满 — 因此,即使文件数据有空间,也无法为另一个文件创建记录。

    现在考虑,为了模拟其中一种情况,您需要操作一些可用于运行测试的进程的文件系统。虽然当然可以在单元测试框架内完成,但它看起来更像是属于集成测试领域。

    有很多选项可以在 Linux 上进行这种级别的测试:"flakey" device-mapper target and friends,通过循环设备或 FUSE 安装只读映像,将故障注入正在运行的内核等。不过,这些大多不适合单元-测试。

    如果您想对这些东西进行单元测试,那么还有两种方法:

    • 按照@Adrien 的建议,使用 https://github.com/spf13/afero 之类的东西抽象出整个文件系统层。

      好处是您可以轻松地测试代码中几乎所有与文件系统相关的内容。

    • 使用变量抽象出一点点代码。

      说,你可能有

      var whateverCreate = os.Create
      

      在您的代码中使用 whateverCreate,然后在测试套件的设置代码中仅覆盖该变量,为其分配一个函数,该函数返回您在特定测试中需要的任何错误。

    【讨论】:

      【解决方案3】:

      您可以使文件名/文件路径可配置,而不是使用硬编码的 info.log,然后在您的测试中您可以使用一些不存在的路径,例如。

      有多种配置方式:

      • 构造函数中的参数(如果您想保持 API 原样,可以从 New 调用一个单独的构造函数)

      • 包级配置(如全局变量 defaultLogFileName),这不太灵活(例如,如果您想并行运行测试),但可能也适合这种情况

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-29
        相关资源
        最近更新 更多