add suite events and possibility to listen to them
Этот коммит содержится в:
родитель
0f6ec04318
коммит
00fab00e22
5 изменённых файлов: 289 добавлений и 62 удалений
|
@ -13,6 +13,13 @@ type Arg struct {
|
|||
value interface{}
|
||||
}
|
||||
|
||||
// StepArgument func creates a step argument.
|
||||
// used in cases when calling another step from
|
||||
// within a StepHandlerFunc
|
||||
func StepArgument(value interface{}) *Arg {
|
||||
return &Arg{value: value}
|
||||
}
|
||||
|
||||
// Float64 converts an argument to float64
|
||||
// or panics if unable to convert it
|
||||
func (a *Arg) Float64() float64 {
|
||||
|
|
103
events.go
Обычный файл
103
events.go
Обычный файл
|
@ -0,0 +1,103 @@
|
|||
package godog
|
||||
|
||||
import "github.com/DATA-DOG/godog/gherkin"
|
||||
|
||||
// BeforeSuiteHandler can be registered
|
||||
// in Suite to be executed once before
|
||||
// running a feature suite
|
||||
type BeforeSuiteHandler interface {
|
||||
HandleBeforeSuite()
|
||||
}
|
||||
|
||||
// BeforeSuiteHandlerFunc is a function implementing
|
||||
// BeforeSuiteHandler interface
|
||||
type BeforeSuiteHandlerFunc func()
|
||||
|
||||
// HandleBeforeSuite is called once before suite
|
||||
func (f BeforeSuiteHandlerFunc) HandleBeforeSuite() {
|
||||
f()
|
||||
}
|
||||
|
||||
// BeforeScenarioHandler can be registered
|
||||
// in Suite to be executed before every scenario
|
||||
// which will be run
|
||||
type BeforeScenarioHandler interface {
|
||||
HandleBeforeScenario(scenario *gherkin.Scenario)
|
||||
}
|
||||
|
||||
// BeforeScenarioHandlerFunc is a function implementing
|
||||
// BeforeScenarioHandler interface
|
||||
type BeforeScenarioHandlerFunc func(scenario *gherkin.Scenario)
|
||||
|
||||
// HandleBeforeScenario is called with a *gherkin.Scenario argument
|
||||
// for before every scenario which is run by suite
|
||||
func (f BeforeScenarioHandlerFunc) HandleBeforeScenario(scenario *gherkin.Scenario) {
|
||||
f(scenario)
|
||||
}
|
||||
|
||||
// BeforeStepHandler can be registered
|
||||
// in Suite to be executed before every step
|
||||
// which will be run
|
||||
type BeforeStepHandler interface {
|
||||
HandleBeforeStep(step *gherkin.Step)
|
||||
}
|
||||
|
||||
// BeforeStepHandlerFunc is a function implementing
|
||||
// BeforeStepHandler interface
|
||||
type BeforeStepHandlerFunc func(step *gherkin.Step)
|
||||
|
||||
// HandleBeforeStep is called with a *gherkin.Step argument
|
||||
// for before every step which is run by suite
|
||||
func (f BeforeStepHandlerFunc) HandleBeforeStep(step *gherkin.Step) {
|
||||
f(step)
|
||||
}
|
||||
|
||||
// AfterStepHandler can be registered
|
||||
// in Suite to be executed after every step
|
||||
// which will be run
|
||||
type AfterStepHandler interface {
|
||||
HandleAfterStep(step *gherkin.Step, status Status)
|
||||
}
|
||||
|
||||
// AfterStepHandlerFunc is a function implementing
|
||||
// AfterStepHandler interface
|
||||
type AfterStepHandlerFunc func(step *gherkin.Step, status Status)
|
||||
|
||||
// HandleAfterStep is called with a *gherkin.Step argument
|
||||
// for after every step which is run by suite
|
||||
func (f AfterStepHandlerFunc) HandleAfterStep(step *gherkin.Step, status Status) {
|
||||
f(step, status)
|
||||
}
|
||||
|
||||
// AfterScenarioHandler can be registered
|
||||
// in Suite to be executed after every scenario
|
||||
// which will be run
|
||||
type AfterScenarioHandler interface {
|
||||
HandleAfterScenario(scenario *gherkin.Scenario, status Status)
|
||||
}
|
||||
|
||||
// AfterScenarioHandlerFunc is a function implementing
|
||||
// AfterScenarioHandler interface
|
||||
type AfterScenarioHandlerFunc func(scenario *gherkin.Scenario, status Status)
|
||||
|
||||
// HandleAfterScenario is called with a *gherkin.Scenario argument
|
||||
// for after every scenario which is run by suite
|
||||
func (f AfterScenarioHandlerFunc) HandleAfterScenario(scenario *gherkin.Scenario, status Status) {
|
||||
f(scenario, status)
|
||||
}
|
||||
|
||||
// AfterSuiteHandler can be registered
|
||||
// in Suite to be executed once after
|
||||
// running a feature suite
|
||||
type AfterSuiteHandler interface {
|
||||
HandleAfterSuite()
|
||||
}
|
||||
|
||||
// AfterSuiteHandlerFunc is a function implementing
|
||||
// AfterSuiteHandler interface
|
||||
type AfterSuiteHandlerFunc func()
|
||||
|
||||
// HandleAfterSuite is called once after suite
|
||||
func (f AfterSuiteHandlerFunc) HandleAfterSuite() {
|
||||
f()
|
||||
}
|
|
@ -4,10 +4,9 @@ Feature: suite hooks
|
|||
I need to provide a way to hook into these events
|
||||
|
||||
Background:
|
||||
Given I have a before scenario hook
|
||||
And a feature path "features/load_features.feature:6"
|
||||
And I parse features
|
||||
Given I'm listening to suite events
|
||||
|
||||
Scenario: triggers before scenario hook
|
||||
When I run features
|
||||
Then I should have a scenario "load features within path" recorded in the hook
|
||||
Given a feature path "features/load_features.feature:6"
|
||||
When I run feature suite
|
||||
Then there was event triggered before scenario "load features within path"
|
||||
|
|
146
suite.go
146
suite.go
|
@ -11,22 +11,27 @@ import (
|
|||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
||||
type stepsStatus int
|
||||
// Status represents a step status
|
||||
type Status int
|
||||
|
||||
const (
|
||||
stepsStatusPassed stepsStatus = iota
|
||||
stepsStatusFailed
|
||||
stepsStatusUndefined
|
||||
invalid Status = iota
|
||||
Passed
|
||||
Failed
|
||||
Undefined
|
||||
)
|
||||
|
||||
type BeforeScenarioHandler interface {
|
||||
BeforeScenario(scenario *gherkin.Scenario)
|
||||
}
|
||||
|
||||
type BeforeScenarioHandlerFunc func(scenario *gherkin.Scenario)
|
||||
|
||||
func (f BeforeScenarioHandlerFunc) BeforeScenario(scenario *gherkin.Scenario) {
|
||||
f(scenario)
|
||||
// String represents status as string
|
||||
func (s Status) String() string {
|
||||
switch s {
|
||||
case Passed:
|
||||
return "passed"
|
||||
case Failed:
|
||||
return "failed"
|
||||
case Undefined:
|
||||
return "undefined"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
// Objects implementing the StepHandler interface can be
|
||||
|
@ -66,16 +71,29 @@ type stepMatchHandler struct {
|
|||
// to register step definitions and event handlers
|
||||
type Suite interface {
|
||||
Step(expr *regexp.Regexp, h StepHandler)
|
||||
// suite events
|
||||
BeforeSuite(h BeforeSuiteHandler)
|
||||
BeforeScenario(h BeforeScenarioHandler)
|
||||
BeforeStep(h BeforeStepHandler)
|
||||
AfterStep(h AfterStepHandler)
|
||||
AfterScenario(h AfterScenarioHandler)
|
||||
AfterSuite(h AfterSuiteHandler)
|
||||
}
|
||||
|
||||
type suite struct {
|
||||
beforeScenarioHandlers []BeforeScenarioHandler
|
||||
stepHandlers []*stepMatchHandler
|
||||
features []*gherkin.Feature
|
||||
fmt Formatter
|
||||
stepHandlers []*stepMatchHandler
|
||||
features []*gherkin.Feature
|
||||
fmt Formatter
|
||||
|
||||
failed bool
|
||||
|
||||
// suite event handlers
|
||||
beforeSuiteHandlers []BeforeSuiteHandler
|
||||
beforeScenarioHandlers []BeforeScenarioHandler
|
||||
beforeStepHandlers []BeforeStepHandler
|
||||
afterStepHandlers []AfterStepHandler
|
||||
afterScenarioHandlers []AfterScenarioHandler
|
||||
afterSuiteHandlers []AfterSuiteHandler
|
||||
}
|
||||
|
||||
// New initializes a suite which supports the Suite
|
||||
|
@ -102,10 +120,42 @@ func (s *suite) Step(expr *regexp.Regexp, h StepHandler) {
|
|||
})
|
||||
}
|
||||
|
||||
// BeforeSuite registers a BeforeSuiteHandler
|
||||
// to be run once before suite runner
|
||||
func (s *suite) BeforeSuite(h BeforeSuiteHandler) {
|
||||
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, h)
|
||||
}
|
||||
|
||||
// BeforeScenario registers a BeforeScenarioHandler
|
||||
// to be run before every scenario
|
||||
func (s *suite) BeforeScenario(h BeforeScenarioHandler) {
|
||||
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, h)
|
||||
}
|
||||
|
||||
// BeforeStep registers a BeforeStepHandler
|
||||
// to be run before every scenario
|
||||
func (s *suite) BeforeStep(h BeforeStepHandler) {
|
||||
s.beforeStepHandlers = append(s.beforeStepHandlers, h)
|
||||
}
|
||||
|
||||
// AfterStep registers an AfterStepHandler
|
||||
// to be run after every scenario
|
||||
func (s *suite) AfterStep(h AfterStepHandler) {
|
||||
s.afterStepHandlers = append(s.afterStepHandlers, h)
|
||||
}
|
||||
|
||||
// AfterScenario registers an AfterScenarioHandler
|
||||
// to be run after every scenario
|
||||
func (s *suite) AfterScenario(h AfterScenarioHandler) {
|
||||
s.afterScenarioHandlers = append(s.afterScenarioHandlers, h)
|
||||
}
|
||||
|
||||
// AfterSuite registers a AfterSuiteHandler
|
||||
// to be run once after suite runner
|
||||
func (s *suite) AfterSuite(h AfterSuiteHandler) {
|
||||
s.afterSuiteHandlers = append(s.afterSuiteHandlers, h)
|
||||
}
|
||||
|
||||
// Run - runs a godog feature suite
|
||||
func (s *suite) Run() {
|
||||
var err error
|
||||
|
@ -129,6 +179,19 @@ func (s *suite) Run() {
|
|||
s.features, err = cfg.features()
|
||||
fatal(err)
|
||||
|
||||
s.run()
|
||||
|
||||
if s.failed {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *suite) run() {
|
||||
// run before suite handlers
|
||||
for _, h := range s.beforeSuiteHandlers {
|
||||
h.HandleBeforeSuite()
|
||||
}
|
||||
// run features
|
||||
for _, f := range s.features {
|
||||
s.runFeature(f)
|
||||
if s.failed && cfg.stopOnFailure {
|
||||
|
@ -136,10 +199,11 @@ func (s *suite) Run() {
|
|||
break
|
||||
}
|
||||
}
|
||||
s.fmt.Summary()
|
||||
if s.failed {
|
||||
os.Exit(1)
|
||||
// run after suite handlers
|
||||
for _, h := range s.afterSuiteHandlers {
|
||||
h.HandleAfterSuite()
|
||||
}
|
||||
s.fmt.Summary()
|
||||
}
|
||||
|
||||
func (s *suite) runStep(step *gherkin.Step) (err error) {
|
||||
|
@ -180,18 +244,31 @@ func (s *suite) runStep(step *gherkin.Step) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *suite) runSteps(steps []*gherkin.Step) (st stepsStatus) {
|
||||
func (s *suite) runSteps(steps []*gherkin.Step) (st Status) {
|
||||
for _, step := range steps {
|
||||
if st != stepsStatusPassed {
|
||||
if st == Failed || st == Undefined {
|
||||
s.fmt.Skipped(step)
|
||||
continue
|
||||
}
|
||||
|
||||
// run before step handlers
|
||||
for _, h := range s.beforeStepHandlers {
|
||||
h.HandleBeforeStep(step)
|
||||
}
|
||||
|
||||
err := s.runStep(step)
|
||||
switch {
|
||||
case err == errPending:
|
||||
st = stepsStatusUndefined
|
||||
case err != nil:
|
||||
st = stepsStatusFailed
|
||||
switch err {
|
||||
case errPending:
|
||||
st = Undefined
|
||||
case nil:
|
||||
st = Passed
|
||||
default:
|
||||
st = Failed
|
||||
}
|
||||
|
||||
// run after step handlers
|
||||
for _, h := range s.afterStepHandlers {
|
||||
h.HandleAfterStep(step, st)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -206,11 +283,11 @@ func (s *suite) skipSteps(steps []*gherkin.Step) {
|
|||
func (s *suite) runFeature(f *gherkin.Feature) {
|
||||
s.fmt.Node(f)
|
||||
for _, scenario := range f.Scenarios {
|
||||
var status stepsStatus
|
||||
var status Status
|
||||
|
||||
// run before scenario handlers
|
||||
for _, h := range s.beforeScenarioHandlers {
|
||||
h.BeforeScenario(scenario)
|
||||
h.HandleBeforeScenario(scenario)
|
||||
}
|
||||
|
||||
// background
|
||||
|
@ -222,15 +299,20 @@ func (s *suite) runFeature(f *gherkin.Feature) {
|
|||
// scenario
|
||||
s.fmt.Node(scenario)
|
||||
switch {
|
||||
case status == stepsStatusFailed:
|
||||
case status == Failed:
|
||||
s.skipSteps(scenario.Steps)
|
||||
case status == stepsStatusUndefined:
|
||||
case status == Undefined:
|
||||
s.skipSteps(scenario.Steps)
|
||||
default:
|
||||
case status == invalid:
|
||||
status = s.runSteps(scenario.Steps)
|
||||
}
|
||||
|
||||
if status == stepsStatusFailed {
|
||||
// run after scenario handlers
|
||||
for _, h := range s.afterScenarioHandlers {
|
||||
h.HandleAfterScenario(scenario, status)
|
||||
}
|
||||
|
||||
if status == Failed {
|
||||
s.failed = true
|
||||
if cfg.stopOnFailure {
|
||||
return
|
||||
|
|
|
@ -3,28 +3,35 @@ package godog
|
|||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/DATA-DOG/godog/gherkin"
|
||||
)
|
||||
|
||||
type suiteFeature struct {
|
||||
suite
|
||||
// for hook tests
|
||||
befScenarioHook *gherkin.Scenario
|
||||
type firedEvent struct {
|
||||
name string
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
func (s *suiteFeature) BeforeScenario(scenario *gherkin.Scenario) {
|
||||
type suiteFeature struct {
|
||||
suite
|
||||
events []*firedEvent
|
||||
}
|
||||
|
||||
func (s *suiteFeature) HandleBeforeScenario(scenario *gherkin.Scenario) {
|
||||
// reset feature paths
|
||||
cfg.paths = []string{}
|
||||
// reset hook test references
|
||||
s.befScenarioHook = nil
|
||||
// reset event stack
|
||||
s.events = []*firedEvent{}
|
||||
// reset formatter, which collects all details
|
||||
s.fmt = &testFormatter{}
|
||||
}
|
||||
|
||||
func (s *suiteFeature) iHaveBeforeScenarioHook(args ...*Arg) error {
|
||||
s.suite.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) {
|
||||
s.befScenarioHook = scenario
|
||||
func (s *suiteFeature) iAmListeningToSuiteEvents(args ...*Arg) error {
|
||||
s.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) {
|
||||
s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{
|
||||
scenario,
|
||||
}})
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
|
@ -59,10 +66,11 @@ func (s *suiteFeature) iShouldHaveNumFeatureFiles(args ...*Arg) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *suiteFeature) iRunFeatures(args ...*Arg) error {
|
||||
for _, f := range s.features {
|
||||
s.runFeature(f)
|
||||
func (s *suiteFeature) iRunFeatureSuite(args ...*Arg) error {
|
||||
if err := s.parseFeatures(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.run()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -77,14 +85,39 @@ func (s *suiteFeature) numScenariosRegistered(args ...*Arg) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *suiteFeature) iShouldHaveScenarioRecordedInHook(args ...*Arg) (err error) {
|
||||
if s.befScenarioHook == nil {
|
||||
return fmt.Errorf("there was no scenario executed in before hook")
|
||||
func (s *suiteFeature) thereWereNumEventsFired(args ...*Arg) error {
|
||||
var num int
|
||||
for _, event := range s.events {
|
||||
if event.name == args[2].String() {
|
||||
num++
|
||||
}
|
||||
}
|
||||
if s.befScenarioHook.Title != args[0].String() {
|
||||
err = fmt.Errorf(`expected "%s" scenario to be run in hook, but got "%s"`, args[0].String(), s.befScenarioHook.Title)
|
||||
if num != args[1].Int() {
|
||||
return fmt.Errorf("expected %d %s events to be fired, but got %d", args[1].Int(), args[2].String(), num)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *suiteFeature) thereWasEventTriggeredBeforeScenario(args ...*Arg) error {
|
||||
var found []string
|
||||
for _, event := range s.events {
|
||||
if event.name != "BeforeScenario" {
|
||||
continue
|
||||
}
|
||||
|
||||
scenario := event.args[0].(*gherkin.Scenario)
|
||||
if scenario.Title == args[0].String() {
|
||||
return nil
|
||||
}
|
||||
|
||||
found = append(found, scenario.Title)
|
||||
}
|
||||
|
||||
if len(found) == 0 {
|
||||
return fmt.Errorf("before scenario event was never triggered or listened")
|
||||
}
|
||||
|
||||
return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, args[0].String(), `"`+strings.Join(found, `", "`)+`"`)
|
||||
}
|
||||
|
||||
func SuiteContext(g Suite) {
|
||||
|
@ -107,12 +140,15 @@ func SuiteContext(g Suite) {
|
|||
regexp.MustCompile(`^I should have ([\d]+) scenarios? registered$`),
|
||||
StepHandlerFunc(s.numScenariosRegistered))
|
||||
g.Step(
|
||||
regexp.MustCompile(`^I have a before scenario hook$`),
|
||||
StepHandlerFunc(s.iHaveBeforeScenarioHook))
|
||||
regexp.MustCompile(`^I'm listening to suite events$`),
|
||||
StepHandlerFunc(s.iAmListeningToSuiteEvents))
|
||||
g.Step(
|
||||
regexp.MustCompile(`^I run features$`),
|
||||
StepHandlerFunc(s.iRunFeatures))
|
||||
regexp.MustCompile(`^I run feature suite$`),
|
||||
StepHandlerFunc(s.iRunFeatureSuite))
|
||||
g.Step(
|
||||
regexp.MustCompile(`^I should have a scenario "([^"]*)" recorded in the hook$`),
|
||||
StepHandlerFunc(s.iShouldHaveScenarioRecordedInHook))
|
||||
regexp.MustCompile(`^there (was|were) ([\d]+) "([^"]*)" events? fired$`),
|
||||
StepHandlerFunc(s.thereWereNumEventsFired))
|
||||
g.Step(
|
||||
regexp.MustCompile(`^there was event triggered before scenario "([^"]*)"$`),
|
||||
StepHandlerFunc(s.thereWasEventTriggeredBeforeScenario))
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче