#32111 添加了 TB.Cleanup
,这对我来说非常有用。然而,通常情况下我实际上有一个 func() error
而不是一个 func()
- 如果出现非空错误,我希望清理失败测试。
现在你可以通过忽略错误来解决这个问题,例如 t.Cleanup(func() { returnsErr() })
,或者类似的东西,它会检查错误并在非空时调用 t.Error
。
我希望 t.CleanupErr
能接受 func() error
,因为在我的经验中这是一个相对常见的场景 - 这样我就可以这样做:t.CleanupErr(returnsErr)
,或者 t.CleanupErr(f.Close)
,或者 t.CleanupErr(cmd.Wait)
,或者 t.CleanupErr(proc.Kill)
等。
如果返回错误,API将调用 TB.Error
而不是 TB.Fatal
,因为我们仍然希望所有清理都运行,即使其中一些失败。如果早期的清理失败并使其他清理也失败,获取更多的错误比跳过一些清理并可能不释放/删除资源要好。
在 #32111 (评论)中,@egonelbre 建议从一开始就让 TB.Cleanup
接受 func() error
,但我不同意这个观点,有趣的是 :) 从那以后我就改变了我的想法。值得注意的是,我仍然认为 func()
形式是有用的 - 许多用例不返回错误,例如 t.Cleanup(cancel)
或 t.Cleanup(func() { global = nil })
。
值得注意的是,这也应该使涉及参数的情况变得更简单,即使仍然需要 func 字面量。例如,
t.Cleanup(func() {
if err := os.Chdir(orig); err != nil {
t.Error(err)
}
})
可以变成只是 t.CleanupErr(func() error { return os.Chdir(orig) })
,避免需要多行。
我还想起了新的 sync.OnceX
API - OnceFunc
接受 func()
,OnceValue
接受 func() T
,OnceValues
接受 func() (T1, T2)
。我认为在这里使用泛型没有意义,因为测试包只能对错误做一些有意义的事情。
9条答案
按热度按时间5t7ly7z51#
作为一条数据,this morning I fixed a bug,其中测试执行了
t.Cleanup(func() { os.Remove(tmpFile) })
,而在调用remove之前我们实际上没有正确关闭文件。在Windows上,这导致了os.Remove
静默失败,因为你不能在文件仍然打开时删除它。由于缺乏错误检查,我们从未注意到这一点,我认为这是由于懒惰造成的,因为进行适当的错误检查只需要五行代码,而不是上面最后一个例子中的一行。相反,我们本可以从一开始就写
t.CleanupErr(func() error { return os.Remove(tmpFile) })
,这只多三个单词。sulc1iza2#
如果这个被批准,我很乐意发送一个CL - 实现很简单。这纯粹是为了减少调用方的冗余。
ugmeyewa3#
另一个例子是在检查错误时链接多个清理步骤。现在,我可能会写:
然而现在我可以写:
von4xj4u4#
魔鬼代言人:这里显然缺少API
t.AssertNilErr
,如果我们不想添加它(我认为我们不想),为什么不呢?这难道不是走向t.AssertNilErr
的一步吗?上下文:我编写了自己的be.NilErr助手。
eimct9ow5#
对不起,我不认为这是一个明显的结论,或者它与这个提案有什么关系。
avkwfej46#
如果有
t.AssertNilErr
,你应该这样写:这感觉有点像在修复一个地方,在那里写
if err != nil { t.Error(err) }
很繁琐,所以最后被跳过,导致测试错误。但是有很多地方写起来很繁琐,所以很多人使用 testify 等工具来避免写它。56lgkhnf7#
就我个人而言,我不同意这个建议——我宁愿为所有的Go代码解决
if err != nil
的问题,就像之前针对try
的提案一样,而不仅仅是针对使用testing.TB
的测试函数。AssertNilErr
感觉就像是一个范围非常狭窄的创可贴。如果通用错误处理能够让
TB.CleanupErr
完全变得不再必要,我会很乐意撤销这个提案。目前还很难说;据我所知,还没有积极地围绕它展开工作。b1payxdu8#
FWIW,使用@dsnet的try package,你可以这样做:
w51jfk4q9#
与标准库的其他部分相比,
testing.T
API似乎更注重简洁地表达常见情况,而不是追求正交性。尽管t.Fail
是严格需要的唯一方法,但已经有许多方法可以让测试失败,而我假设这是为了帮助测试代码相对简洁,避免分散注意力。我同意这个评估,大多数时候我在测试中需要清理工作,这是一些容易出错的事情,比如删除一些临时文件或终止一个子进程,所以我同意这似乎是一个足够常见的情况,值得用一种简洁的方式来写它,而且有一种简洁的方式来写它很可能有助于作者编写正确的测试,当他们的清理步骤失败时会失败。
由于清理的概念相对较新,我花费大部分时间在的代码库还没有普遍采用它,但有一些现有的使用
defer
语句用于类似的目的,在我的经验中,这些通常也会忽略错误,因为测试作者优先考虑简洁性。我认为添加这样的提案将激励在这些代码库中更积极地采用新的API,而不是defer
,从而使测试用例更加健壮,而不会导致显著增加冗余。