将断言库与 GoDog 测试框架一起使用

Using an assertion library with GoDog test framework

我正在使用 Cucumber GoDog 作为 gRPC 微服务测试的 BDD 测试框架。 GoDog 不附带任何断言助手或实用程序。

这里有没有人有在 GoDog 中采用任何现有断言库的经验,例如 Testify/GoMega

据我所知,GoDog 不能在 go test 之上工作,这就是为什么我认为像我提到的那样采用任何基于 go test 的断言库都具有挑战性。不过还是想看看有没有人有这方面的经验

很抱歉看到您仍在为此努力。

正如我们之前聊过的那样,一种通过我之前发给你的the link让它工作的方法,它不一定像你提到的那样适合初学者在松弛。也许这是我们的贡献者将来可以考虑的事情,只是目前还没有设置,而且由于我们主要是志愿者,因此为新功能设置时间表可能很困难。

我目前的建议是通过 if 语句进行断言。如果您不想在您的测试代码中专门使用它们,那么您可以制作一个快速包装函数并以这种方式调用它们。

这是使用 Testify 的基本概念验证:

package bdd
import (
    "fmt"
    "github.com/cucumber/godog"
    "github.com/stretchr/testify/assert"
)
type scenario struct{}
func (_ *scenario) assert(a assertion, expected, actual interface{}, msgAndArgs ...interface{}) error {
    var t asserter
    a(&t, expected, actual, msgAndArgs...)
    return t.err
}
func (sc *scenario) forcedFailure() error {
    return sc.assert(assert.Equal, 1, 2)
}
type assertion func(t assert.TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
type asserter struct {
    err error
}
func (a *asserter) Errorf(format string, args ...interface{}) {
    a.err = fmt.Errorf(format, args...)
}
func FeatureContext(s *godog.Suite) {
    var sc scenario
    s.Step("^forced failure$", sc.forcedFailure)
}
Feature: forced failure
  Scenario: fail
    Then forced failure

这里的关键是实现 Testify 的 assert.TestingT 接口。

这是 GoMega 的概念证明:

在 运行 任何测试之前注册 GoMega 失败处理程序,让 GoMega 简单地恐慌并显示错误消息。

gomega.RegisterFailHandler(func(message string, _ ...int) {
    panic(message)
})

定义步骤失败处理程序以从任何失败中恢复。

func failHandler(err *error) {
    if r := recover(); r != nil {
        *err = fmt.Errorf("%s", r)
    }
}

现在在每个步骤定义的开头延迟 运行 像这样设置 failHandler:

func shouldBeBar(foo string) (err error) {
    defer failHandler(&err)

    Expect(foo).Should(Equal("bar"))

    return err
}

现在 if/when 我们的第一个 GoMega 断言失败,step 函数将 运行 failHandler 和 return GoMega 失败消息(如果有的话)。请注意,我们正在使用命名结果参数来 return 错误,请参阅

今天有同样的问题,试图将gomega与godog集成。由于 Go 的简单性,我设法让一些东西工作(这是我使用 Go 的第三天 :-)。尽管我认为这在现实世界的项目中行不通,但我还是想分享一下我的想法。

来自 Rails/RSpec,我喜欢没有样板代码的紧凑测试 cases/steps。所以我试着把处理失败的步骤放到 before/after 钩子中:

func InitializeGomegaForGodog(ctx *godog.ScenarioContext) {
    var testResult error
    ctx.StepContext().Before(func(ctx context.Context, st *godog.Step) (context.Context, error) {
        testResult = nil
        return ctx, nil
    })
    ctx.StepContext().After(func(ctx context.Context, st *godog.Step, status godog.StepResultStatus, err error) (context.Context, error) {
        return ctx, testResult
    })
    gomega.RegisterFailHandler(func(message string, callerSkip ...int) {
        // remember only the expectation failed first
        // anything thereafter is not to be believed
        if testResult == nil {
            testResult = fmt.Errorf(message)
        }
    })
}

func InitializeScenario(ctx *godog.ScenarioContext) {
    InitializeGomegaForGodog(ctx)

    ctx.Step(`^incrementing (\d+)$`, incrementing)
    ctx.Step(`^result is (\d+)$`, resultIs)
}

当然,这种方法不会停止与预期不符的步骤。因此,在步骤的其余部分存在未定义行为的风险。但是使用这种方法,步骤的实现非常简单:

func resultIs(arg1 int) {
    gomega.Expect(1000).To(gomega.Equal(arg1))
}