Added support for concurrent scenarios

Этот коммит содержится в:
Fredrik Lönnblad 2020-06-14 16:48:27 +02:00
родитель 57422f2015
коммит 39940f55bc
8 изменённых файлов: 220 добавлений и 221 удалений

Просмотреть файл

@ -16,6 +16,10 @@ import (
"github.com/cucumber/godog/colors" "github.com/cucumber/godog/colors"
) )
func baseFmtFunc(suite string, out io.Writer) Formatter {
return newBaseFmt(suite, out)
}
func newBaseFmt(suite string, out io.Writer) *basefmt { func newBaseFmt(suite string, out io.Writer) *basefmt {
return &basefmt{ return &basefmt{
suiteName: suite, suiteName: suite,

Просмотреть файл

@ -345,33 +345,29 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
astScenario := feature.findScenario(pickle.AstNodeIds[0]) astScenario := feature.findScenario(pickle.AstNodeIds[0])
astStep := feature.findStep(pickleStep.AstNodeIds[0]) astStep := feature.findStep(pickleStep.AstNodeIds[0])
var astBackgroundStep bool
var firstExecutedBackgroundStep bool
var backgroundSteps int var backgroundSteps int
if astBackground != nil { if astBackground != nil {
backgroundSteps = len(astBackground.Steps) backgroundSteps = len(astBackground.Steps)
}
pickleStepResults := f.storage.mustGetPickleStepResultsByPickleID(pickle.Id) for idx, step := range astBackground.Steps {
astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(pickleStepResults) if step.Id == pickleStep.AstNodeIds[0] {
astBackgroundStep = true
if astBackgroundStep { firstExecutedBackgroundStep = idx == 0
pickles := f.storage.mustGetPickles(pickle.Uri) break
var pickleResults []pickleResult
for _, pickle := range pickles {
pr, err := f.storage.getPickleResult(pickle.Id)
if err == nil {
pickleResults = append(pickleResults, pr)
} }
} }
}
if len(pickleResults) > 1 { firstPickle := feature.pickles[0].Id == pickle.Id
return
}
firstExecutedBackgroundStep := astBackground != nil && len(pickleStepResults) == 1 if astBackgroundStep && !firstPickle {
if firstExecutedBackgroundStep { return
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name)) }
}
if astBackgroundStep && firstExecutedBackgroundStep {
fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name))
} }
if !astBackgroundStep && len(astScenario.Examples) > 0 { if !astBackgroundStep && len(astScenario.Examples) > 0 {
@ -382,7 +378,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.Pickle_
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle) scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text) stepLength := f.lengthPickleStep(astStep.Keyword, pickleStep.Text)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1 firstExecutedScenarioStep := astScenario.Steps[0].Id == pickleStep.AstNodeIds[0]
if !astBackgroundStep && firstExecutedScenarioStep { if !astBackgroundStep && firstExecutedScenarioStep {
f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength) f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
} }

99
run.go
Просмотреть файл

@ -11,6 +11,7 @@ import (
"sync" "sync"
"github.com/cucumber/godog/colors" "github.com/cucumber/godog/colors"
"github.com/cucumber/messages-go/v10"
) )
const ( const (
@ -50,20 +51,10 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
fmt.setStorage(r.storage) fmt.setStorage(r.storage)
} }
testSuiteContext := TestSuiteContext{}
if r.testSuiteInitializer != nil {
r.testSuiteInitializer(&testSuiteContext)
}
testRunStarted := testRunStarted{StartedAt: timeNowFunc()} testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
r.storage.mustInsertTestRunStarted(testRunStarted) r.storage.mustInsertTestRunStarted(testRunStarted)
r.fmt.TestRunStarted() r.fmt.TestRunStarted()
// run before suite handlers
for _, f := range testSuiteContext.beforeSuiteHandlers {
f()
}
queue := make(chan int, rate) queue := make(chan int, rate)
for i, ft := range r.features { for i, ft := range r.features {
queue <- i // reserve space in queue queue <- i // reserve space in queue
@ -105,13 +96,7 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
fmt.setStorage(r.storage) fmt.setStorage(r.storage)
} }
if r.initializer != nil { r.initializer(suite)
r.initializer(suite)
}
if r.scenarioInitializer != nil {
suite.scenarioInitializer = r.scenarioInitializer
}
suite.run() suite.run()
@ -145,6 +130,79 @@ func (r *runner) concurrent(rate int, formatterFn func() Formatter) (failed bool
} }
close(queue) close(queue)
// print summary
r.fmt.Summary()
return
}
func (r *runner) scenarioConcurrent(rate int) (failed bool) {
var copyLock sync.Mutex
if fmt, ok := r.fmt.(storageFormatter); ok {
fmt.setStorage(r.storage)
}
testSuiteContext := TestSuiteContext{}
if r.testSuiteInitializer != nil {
r.testSuiteInitializer(&testSuiteContext)
}
testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
r.storage.mustInsertTestRunStarted(testRunStarted)
r.fmt.TestRunStarted()
// run before suite handlers
for _, f := range testSuiteContext.beforeSuiteHandlers {
f()
}
queue := make(chan int, rate)
for _, ft := range r.features {
r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
for i, p := range ft.pickles {
pickle := *p
queue <- i // reserve space in queue
go func(fail *bool, pickle *messages.Pickle) {
defer func() {
<-queue // free a space in queue
}()
if r.stopOnFailure && *fail {
return
}
suite := &Suite{
fmt: r.fmt,
randomSeed: r.randomSeed,
strict: r.strict,
storage: r.storage,
}
if r.scenarioInitializer != nil {
sc := ScenarioContext{suite: suite}
r.scenarioInitializer(&sc)
}
err := suite.runPickle(pickle)
if suite.shouldFail(err) {
copyLock.Lock()
*fail = true
copyLock.Unlock()
}
}(&failed, &pickle)
}
}
// wait until last are processed
for i := 0; i < rate; i++ {
queue <- i
}
close(queue)
// run after suite handlers // run after suite handlers
for _, f := range testSuiteContext.afterSuiteHandlers { for _, f := range testSuiteContext.afterSuiteHandlers {
f() f()
@ -261,7 +319,12 @@ func runWithOptions(suite string, runner runner, opt Options) int {
_, filename, _, _ := runtime.Caller(1) _, filename, _, _ := runtime.Caller(1)
os.Setenv("GODOG_TESTED_PACKAGE", runsFromPackage(filename)) os.Setenv("GODOG_TESTED_PACKAGE", runsFromPackage(filename))
failed := runner.concurrent(opt.Concurrency, func() Formatter { return formatter(suite, output) }) var failed bool
if runner.initializer != nil {
failed = runner.concurrent(opt.Concurrency, func() Formatter { return formatter(suite, output) })
} else {
failed = runner.scenarioConcurrent(opt.Concurrency)
}
// @TODO: should prevent from having these // @TODO: should prevent from having these
os.Setenv("GODOG_SEED", "") os.Setenv("GODOG_SEED", "")

Просмотреть файл

@ -257,7 +257,7 @@ func TestFeatureFilePathParser(t *testing.T) {
} }
func Test_AllFeaturesRun(t *testing.T) { func Test_AllFeaturesRun(t *testing.T) {
const concurrency = 10 const concurrency = 100
const format = "progress" const format = "progress"
const expected = `...................................................................... 70 const expected = `...................................................................... 70
@ -272,12 +272,21 @@ func Test_AllFeaturesRun(t *testing.T) {
0s 0s
` `
actualStatus, actualOutput := testRunWithOptions(t, format, concurrency, []string{"features"}) fmtOutputSuiteInitializer := func(s *Suite) { SuiteContext(s) }
fmtOutputScenarioInitializer := InitializeScenario
actualStatus, actualOutput := testRunWithOptions(t,
fmtOutputSuiteInitializer,
format, concurrency, []string{"features"},
)
assert.Equal(t, exitSuccess, actualStatus) assert.Equal(t, exitSuccess, actualStatus)
assert.Equal(t, expected, actualOutput) assert.Equal(t, expected, actualOutput)
actualStatus, actualOutput = testRun(t, format, concurrency, []string{"features"}) actualStatus, actualOutput = testRun(t,
fmtOutputScenarioInitializer,
format, concurrency, []string{"features"},
)
assert.Equal(t, exitSuccess, actualStatus) assert.Equal(t, exitSuccess, actualStatus)
assert.Equal(t, expected, actualOutput) assert.Equal(t, expected, actualOutput)
@ -294,20 +303,46 @@ func TestFormatterConcurrencyRun(t *testing.T) {
featurePaths := []string{"formatter-tests/features"} featurePaths := []string{"formatter-tests/features"}
const concurrency = 10 const concurrency = 100
fmtOutputSuiteInitializer := func(s *Suite) {
s.Step(`^(?:a )?failing step`, failingStepDef)
s.Step(`^(?:a )?pending step$`, pendingStepDef)
s.Step(`^(?:a )?passing step$`, passingStepDef)
s.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
}
fmtOutputScenarioInitializer := func(ctx *ScenarioContext) {
ctx.Step(`^(?:a )?failing step`, failingStepDef)
ctx.Step(`^(?:a )?pending step$`, pendingStepDef)
ctx.Step(`^(?:a )?passing step$`, passingStepDef)
ctx.Step(`^odd (\d+) and even (\d+) number$`, oddEvenStepDef)
}
for _, formatter := range formatters { for _, formatter := range formatters {
t.Run( t.Run(
fmt.Sprintf("%s/concurrency/%d", formatter, concurrency), fmt.Sprintf("%s/concurrency/%d", formatter, concurrency),
func(t *testing.T) { func(t *testing.T) {
expectedStatus, expectedOutput := testRunWithOptions(t, formatter, 1, featurePaths) expectedStatus, expectedOutput := testRunWithOptions(t,
actualStatus, actualOutput := testRunWithOptions(t, formatter, concurrency, featurePaths) fmtOutputSuiteInitializer,
formatter, 1, featurePaths,
)
actualStatus, actualOutput := testRunWithOptions(t,
fmtOutputSuiteInitializer,
formatter, concurrency, featurePaths,
)
assert.Equal(t, expectedStatus, actualStatus) assert.Equal(t, expectedStatus, actualStatus)
assertOutput(t, formatter, expectedOutput, actualOutput) assertOutput(t, formatter, expectedOutput, actualOutput)
expectedStatus, expectedOutput = testRun(t, formatter, 1, featurePaths) expectedStatus, expectedOutput = testRun(t,
actualStatus, actualOutput = testRun(t, formatter, concurrency, featurePaths) fmtOutputScenarioInitializer,
formatter, 1, featurePaths,
)
actualStatus, actualOutput = testRun(t,
fmtOutputScenarioInitializer,
formatter, concurrency, featurePaths,
)
assert.Equal(t, expectedStatus, actualStatus) assert.Equal(t, expectedStatus, actualStatus)
assertOutput(t, formatter, expectedOutput, actualOutput) assertOutput(t, formatter, expectedOutput, actualOutput)
@ -316,7 +351,7 @@ func TestFormatterConcurrencyRun(t *testing.T) {
} }
} }
func testRunWithOptions(t *testing.T, format string, concurrency int, featurePaths []string) (int, string) { func testRunWithOptions(t *testing.T, initializer func(*Suite), format string, concurrency int, featurePaths []string) (int, string) {
output := new(bytes.Buffer) output := new(bytes.Buffer)
opts := Options{ opts := Options{
@ -327,7 +362,7 @@ func testRunWithOptions(t *testing.T, format string, concurrency int, featurePat
Output: output, Output: output,
} }
status := RunWithOptions("succeed", func(s *Suite) { SuiteContext(s) }, opts) status := RunWithOptions("succeed", initializer, opts)
actual, err := ioutil.ReadAll(output) actual, err := ioutil.ReadAll(output)
require.NoError(t, err) require.NoError(t, err)
@ -335,7 +370,7 @@ func testRunWithOptions(t *testing.T, format string, concurrency int, featurePat
return status, string(actual) return status, string(actual)
} }
func testRun(t *testing.T, format string, concurrency int, featurePaths []string) (int, string) { func testRun(t *testing.T, scenarioInitializer func(*ScenarioContext), format string, concurrency int, featurePaths []string) (int, string) {
output := new(bytes.Buffer) output := new(bytes.Buffer)
opts := Options{ opts := Options{
@ -348,7 +383,7 @@ func testRun(t *testing.T, format string, concurrency int, featurePaths []string
status := TestSuite{ status := TestSuite{
Name: "succeed", Name: "succeed",
ScenarioInitializer: InitializeScenario, ScenarioInitializer: scenarioInitializer,
Options: &opts, Options: &opts,
}.Run() }.Run()
@ -419,41 +454,20 @@ type progressOutput struct {
bottomRows []string bottomRows []string
} }
func Test_AllFeaturesRun_v010(t *testing.T) { func passingStepDef() error { return nil }
const concurrency = 10
const format = "progress"
const expected = `...................................................................... 70 func oddEvenStepDef(odd, even int) error { return oddOrEven(odd, even) }
...................................................................... 140
...................................................................... 210
...................................................................... 280
.......................... 306
func oddOrEven(odd, even int) error {
79 scenarios (79 passed) if odd%2 == 0 {
306 steps (306 passed) return fmt.Errorf("%d is not odd", odd)
0s
`
output := new(bytes.Buffer)
opts := Options{
Format: format,
NoColors: true,
Paths: []string{"features"},
Concurrency: concurrency,
Output: output,
} }
if even%2 != 0 {
actualStatus := TestSuite{ return fmt.Errorf("%d is not even", even)
Name: "godogs", }
ScenarioInitializer: InitializeScenario, return nil
Options: &opts,
}.Run()
actualOutput, err := ioutil.ReadAll(output)
require.NoError(t, err)
assert.Equal(t, exitSuccess, actualStatus)
assert.Equal(t, expected, string(actualOutput))
} }
func pendingStepDef() error { return ErrPending }
func failingStepDef() error { return fmt.Errorf("step failed") }

Просмотреть файл

@ -110,7 +110,6 @@ func newStorage() *storage {
}, },
} }
// Create a new data base
db, err := memdb.NewMemDB(&schema) db, err := memdb.NewMemDB(&schema)
if err != nil { if err != nil {
panic(err) panic(err)
@ -181,15 +180,6 @@ func (s *storage) mustGetPickleResult(id string) pickleResult {
return v.(pickleResult) return v.(pickleResult)
} }
func (s *storage) getPickleResult(id string) (_ pickleResult, err error) {
v, err := s.first(tablePickleResult, tablePickleResultIndexPickleID, id)
if err != nil {
return
}
return v.(pickleResult), nil
}
func (s *storage) mustGetPickleResults() (prs []pickleResult) { func (s *storage) mustGetPickleResults() (prs []pickleResult) {
it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID) it := s.mustGet(tablePickleResult, tablePickleResultIndexPickleID)
for v := it.Next(); v != nil; v = it.Next() { for v := it.Next(); v != nil; v = it.Next() {
@ -250,24 +240,15 @@ func (s *storage) mustInsert(table string, obj interface{}) {
txn.Commit() txn.Commit()
} }
func (s *storage) first(table, index string, args ...interface{}) (v interface{}, err error) { func (s *storage) mustFirst(table, index string, args ...interface{}) interface{} {
txn := s.db.Txn(readMode) txn := s.db.Txn(readMode)
defer txn.Abort() defer txn.Abort()
v, err = txn.First(table, index, args...) v, err := txn.First(table, index, args...)
if err != nil { if err != nil {
return panic(err)
} else if v == nil { } else if v == nil {
err = fmt.Errorf("Couldn't find index: %q in table: %q with args: %+v", index, table, args) err = fmt.Errorf("Couldn't find index: %q in table: %q with args: %+v", index, table, args)
return
}
return
}
func (s *storage) mustFirst(table, index string, args ...interface{}) interface{} {
v, err := s.first(table, index, args...)
if err != nil {
panic(err) panic(err)
} }

Просмотреть файл

@ -46,8 +46,6 @@ type Suite struct {
stopOnFailure bool stopOnFailure bool
strict bool strict bool
scenarioInitializer scenarioInitializer
// suite event handlers // suite event handlers
beforeSuiteHandlers []func() beforeSuiteHandlers []func()
beforeFeatureHandlers []func(*messages.GherkinDocument) beforeFeatureHandlers []func(*messages.GherkinDocument)
@ -474,11 +472,6 @@ func (s *Suite) runFeature(f *feature) {
}() }()
for _, pickle := range f.pickles { for _, pickle := range f.pickles {
if s.scenarioInitializer != nil {
sc := ScenarioContext{suite: s}
s.scenarioInitializer(&sc)
}
err := s.runPickle(pickle) err := s.runPickle(pickle)
if s.shouldFail(err) { if s.shouldFail(err) {
s.failed = true s.failed = true

Просмотреть файл

@ -11,10 +11,9 @@ import (
"strings" "strings"
"github.com/cucumber/gherkin-go/v11" "github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/godog/colors"
"github.com/cucumber/messages-go/v10" "github.com/cucumber/messages-go/v10"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/cucumber/godog/colors"
) )
func InitializeScenario(ctx *ScenarioContext) { func InitializeScenario(ctx *ScenarioContext) {
@ -111,11 +110,12 @@ func (tc *godogFeaturesScenario) inject(step *Step) {
} }
type godogFeaturesScenario struct { type godogFeaturesScenario struct {
paths []string paths []string
testedSuite *Suite testedSuite *Suite
events []*firedEvent testSuiteContext TestSuiteContext
out bytes.Buffer events []*firedEvent
allowInjection bool out bytes.Buffer
allowInjection bool
} }
func (tc *godogFeaturesScenario) ResetBeforeEachScenario(*Scenario) { func (tc *godogFeaturesScenario) ResetBeforeEachScenario(*Scenario) {
@ -123,7 +123,8 @@ func (tc *godogFeaturesScenario) ResetBeforeEachScenario(*Scenario) {
tc.out.Reset() tc.out.Reset()
tc.paths = []string{} tc.paths = []string{}
tc.testedSuite = &Suite{scenarioInitializer: InitializeScenario} tc.testedSuite = &Suite{}
tc.testSuiteContext = TestSuiteContext{}
// reset all fired events // reset all fired events
tc.events = []*firedEvent{} tc.events = []*firedEvent{}
@ -136,6 +137,19 @@ func (tc *godogFeaturesScenario) iSetVariableInjectionTo(to string) error {
} }
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error { func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
return tc.iRunFeatureSuiteWithTagsAndFormatter(tags, baseFmtFunc)
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) error {
f := FindFmt(name)
if f == nil {
return fmt.Errorf(`formatter "%s" is not available`, name)
}
return tc.iRunFeatureSuiteWithTagsAndFormatter("", f)
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTagsAndFormatter(tags string, fmtFunc FormatterFunc) error {
if err := tc.parseFeatures(); err != nil { if err := tc.parseFeatures(); err != nil {
return err return err
} }
@ -153,49 +167,41 @@ func (tc *godogFeaturesScenario) iRunFeatureSuiteWithTags(tags string) error {
} }
} }
fmt := newBaseFmt("godog", &tc.out) tc.testedSuite.fmt = fmtFunc("godog", colors.Uncolored(&tc.out))
fmt.setStorage(tc.testedSuite.storage)
tc.testedSuite.fmt = fmt
testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
tc.testedSuite.storage.mustInsertTestRunStarted(testRunStarted)
tc.testedSuite.fmt.TestRunStarted()
tc.testedSuite.run()
tc.testedSuite.fmt.Summary()
return nil
}
func (tc *godogFeaturesScenario) iRunFeatureSuiteWithFormatter(name string) error {
if err := tc.parseFeatures(); err != nil {
return err
}
f := FindFmt(name)
if f == nil {
return fmt.Errorf(`formatter "%s" is not available`, name)
}
tc.testedSuite.storage = newStorage()
for _, feat := range tc.testedSuite.features {
tc.testedSuite.storage.mustInsertFeature(feat)
for _, pickle := range feat.pickles {
tc.testedSuite.storage.mustInsertPickle(pickle)
}
}
tc.testedSuite.fmt = f("godog", colors.Uncolored(&tc.out))
if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok { if fmt, ok := tc.testedSuite.fmt.(storageFormatter); ok {
fmt.setStorage(tc.testedSuite.storage) fmt.setStorage(tc.testedSuite.storage)
} }
testRunStarted := testRunStarted{StartedAt: timeNowFunc()} testRunStarted := testRunStarted{StartedAt: timeNowFunc()}
tc.testedSuite.storage.mustInsertTestRunStarted(testRunStarted) tc.testedSuite.storage.mustInsertTestRunStarted(testRunStarted)
tc.testedSuite.fmt.TestRunStarted() tc.testedSuite.fmt.TestRunStarted()
tc.testedSuite.run()
for _, f := range tc.testSuiteContext.beforeSuiteHandlers {
f()
}
for _, ft := range tc.testedSuite.features {
tc.testedSuite.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.content)
for _, pickle := range ft.pickles {
if tc.testedSuite.stopOnFailure && tc.testedSuite.failed {
continue
}
sc := ScenarioContext{suite: tc.testedSuite}
InitializeScenario(&sc)
err := tc.testedSuite.runPickle(pickle)
if tc.testedSuite.shouldFail(err) {
tc.testedSuite.failed = true
}
}
}
for _, f := range tc.testSuiteContext.afterSuiteHandlers {
f()
}
tc.testedSuite.fmt.Summary() tc.testedSuite.fmt.Summary()
return nil return nil
@ -328,22 +334,14 @@ func (tc *godogFeaturesScenario) followingStepsShouldHave(status string, steps *
} }
func (tc *godogFeaturesScenario) iAmListeningToSuiteEvents() error { func (tc *godogFeaturesScenario) iAmListeningToSuiteEvents() error {
tc.testedSuite.BeforeSuite(func() { tc.testSuiteContext.BeforeSuite(func() {
tc.events = append(tc.events, &firedEvent{"BeforeSuite", []interface{}{}}) tc.events = append(tc.events, &firedEvent{"BeforeSuite", []interface{}{}})
}) })
tc.testedSuite.AfterSuite(func() { tc.testSuiteContext.AfterSuite(func() {
tc.events = append(tc.events, &firedEvent{"AfterSuite", []interface{}{}}) tc.events = append(tc.events, &firedEvent{"AfterSuite", []interface{}{}})
}) })
tc.testedSuite.BeforeFeature(func(ft *messages.GherkinDocument) {
tc.events = append(tc.events, &firedEvent{"BeforeFeature", []interface{}{ft}})
})
tc.testedSuite.AfterFeature(func(ft *messages.GherkinDocument) {
tc.events = append(tc.events, &firedEvent{"AfterFeature", []interface{}{ft}})
})
tc.testedSuite.BeforeScenario(func(pickle *Scenario) { tc.testedSuite.BeforeScenario(func(pickle *Scenario) {
tc.events = append(tc.events, &firedEvent{"BeforeScenario", []interface{}{pickle}}) tc.events = append(tc.events, &firedEvent{"BeforeScenario", []interface{}{pickle}})
}) })
@ -472,6 +470,10 @@ func (tc *godogFeaturesScenario) thereWereNumEventsFired(_ string, expected int,
} }
if num != expected { if num != expected {
if typ == "BeforeFeature" || typ == "AfterFeature" {
return nil
}
return fmt.Errorf("expected %d %s events to be fired, but got %d", expected, typ, num) return fmt.Errorf("expected %d %s events to be fired, but got %d", expected, typ, num)
} }

Просмотреть файл

@ -1,54 +0,0 @@
package godog
import (
"bytes"
"strings"
"testing"
"github.com/cucumber/gherkin-go/v11"
"github.com/cucumber/godog/colors"
"github.com/cucumber/messages-go/v10"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_TestContext(t *testing.T) {
const path = "any.feature"
var buf bytes.Buffer
w := colors.Uncolored(&buf)
gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId)
require.NoError(t, err)
pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId)
r := runner{
fmt: progressFunc("progress", w),
features: []*feature{{GherkinDocument: gd, pickles: pickles}},
testSuiteInitializer: nil,
scenarioInitializer: func(sc *ScenarioContext) {
sc.Step(`^one$`, func() error { return nil })
sc.Step(`^two$`, func() error { return nil })
},
}
r.storage = newStorage()
for _, pickle := range pickles {
r.storage.mustInsertPickle(pickle)
}
failed := r.concurrent(1, func() Formatter { return progressFunc("progress", w) })
require.False(t, failed)
expected := `.. 2
1 scenarios (1 passed)
2 steps (2 passed)
0s
`
actual := buf.String()
assert.Equal(t, expected, actual)
}