如何跳过失败的测试
How to skip a failed test
在 go 中,您可以跳过已经失败的测试吗?
上下文:
我有一个 heisenbug 目前无法确定其原因。它导致某些测试有时会失败。
通过检查各种日志,我可以确定故障模式。我想做类似的事情:
if t.Failed() {
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
}
这些测试非常有价值,我想 运行 在 CI 中进行测试,尽管存在 heisenbug,所以这只是一个临时解决方法。
这行不通。如果测试失败,有没有办法追溯跳过测试?
简短的回答是否。您可以跳过测试或不通过测试,但不能同时进行。
go 的设计者将试图跳过测试视为试图颠覆测试框架
所以你不应该尝试这样做:
参见示例https://github.com/golang/go/issues/16502
这是 documented 但很容易错过:
If a test fails (see Error, Errorf, Fail) and is then skipped, it is still considered to have failed.
如果您有检测 heisenbug 的可靠方法,您应该在做出任何测试断言之前 运行 它。所以而不是:
// execute
executeThingBeingTested()
// verify
assert.Equal(t, expected, actual)
// recover if needed
if t.Failed() {
// detect heisenbug
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
}
你应该像这样构建你的测试:
// execute
executeThingBeingTested()
// skip if needed
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
// verify
assert.Equal(t, expected, actual)
这意味着您不能在单个测试中的多个执行和验证阶段之间交替,但无论如何,在每个测试中只有一个执行和验证阶段是一种很好的做法。即 four phase testing
现在如果你真的真的想做,你可以去低级。这可能不是一个好主意,但为了完整性而包含在内。窥视兔子洞可能有助于表明你不想去那里。这考虑到了 this question and how the testing
package is implemented
t := suite.T()
// low-level hackery - undo the failed state so we can skip a test
pointerVal := reflect.ValueOf(t)
val := reflect.Indirect(pointerVal)
member := val.FieldByName("failed")
ptrToFailedFlag := unsafe.Pointer(member.UnsafeAddr())
realPtrToFailedFlag := (*bool)(ptrToFailedFlag)
*realPtrToFailedFlag = false
如果这种黑客水平不足以让您相信这是个多么糟糕的主意,您可能需要在撰写本文时注意 fail()
的实现:
// Fail marks the function as having failed but continues execution.
605 func (c *common) Fail() {
606 if c.parent != nil {
607 c.parent.Fail()
608 }
609 c.mu.Lock()
610 defer c.mu.Unlock()
611 // c.done needs to be locked to synchronize checks to c.done in parent tests.
612 if c.done {
613 panic("Fail in goroutine after " + c.name + " has completed")
614 }
615 c.failed = true
616 }
您可以看到,只要 Fail() 被调用,任何父测试也会被标记为失败。因此,如果您使用 testify/suite 之类的方法将测试组织到套件中,对于 unfail
测试,您还必须 unfail
父测试,但 当且仅当 套件中没有其他测试失败。因此,更改 testing() 包以允许在失败后发生跳过与嵌套测试的想法相互作用不佳。
在 go 中,您可以跳过已经失败的测试吗?
上下文:
我有一个 heisenbug 目前无法确定其原因。它导致某些测试有时会失败。 通过检查各种日志,我可以确定故障模式。我想做类似的事情:
if t.Failed() {
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
}
这些测试非常有价值,我想 运行 在 CI 中进行测试,尽管存在 heisenbug,所以这只是一个临时解决方法。
这行不通。如果测试失败,有没有办法追溯跳过测试?
简短的回答是否。您可以跳过测试或不通过测试,但不能同时进行。
go 的设计者将试图跳过测试视为试图颠覆测试框架 所以你不应该尝试这样做:
参见示例https://github.com/golang/go/issues/16502
这是 documented 但很容易错过:
If a test fails (see Error, Errorf, Fail) and is then skipped, it is still considered to have failed.
如果您有检测 heisenbug 的可靠方法,您应该在做出任何测试断言之前 运行 它。所以而不是:
// execute
executeThingBeingTested()
// verify
assert.Equal(t, expected, actual)
// recover if needed
if t.Failed() {
// detect heisenbug
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
}
你应该像这样构建你的测试:
// execute
executeThingBeingTested()
// skip if needed
if strings.Contains(string(suite.Stdout), "connection reset by peer") {
t.Skip("Skip Test failed ")
}
// verify
assert.Equal(t, expected, actual)
这意味着您不能在单个测试中的多个执行和验证阶段之间交替,但无论如何,在每个测试中只有一个执行和验证阶段是一种很好的做法。即 four phase testing
现在如果你真的真的想做,你可以去低级。这可能不是一个好主意,但为了完整性而包含在内。窥视兔子洞可能有助于表明你不想去那里。这考虑到了 this question and how the testing
package is implemented
t := suite.T()
// low-level hackery - undo the failed state so we can skip a test
pointerVal := reflect.ValueOf(t)
val := reflect.Indirect(pointerVal)
member := val.FieldByName("failed")
ptrToFailedFlag := unsafe.Pointer(member.UnsafeAddr())
realPtrToFailedFlag := (*bool)(ptrToFailedFlag)
*realPtrToFailedFlag = false
如果这种黑客水平不足以让您相信这是个多么糟糕的主意,您可能需要在撰写本文时注意 fail()
的实现:
// Fail marks the function as having failed but continues execution.
605 func (c *common) Fail() {
606 if c.parent != nil {
607 c.parent.Fail()
608 }
609 c.mu.Lock()
610 defer c.mu.Unlock()
611 // c.done needs to be locked to synchronize checks to c.done in parent tests.
612 if c.done {
613 panic("Fail in goroutine after " + c.name + " has completed")
614 }
615 c.failed = true
616 }
您可以看到,只要 Fail() 被调用,任何父测试也会被标记为失败。因此,如果您使用 testify/suite 之类的方法将测试组织到套件中,对于 unfail
测试,您还必须 unfail
父测试,但 当且仅当 套件中没有其他测试失败。因此,更改 testing() 包以允许在失败后发生跳过与嵌套测试的想法相互作用不佳。