godog/suite.go
2015-06-17 13:54:31 +03:00

188 строки
4,2 КиБ
Go

package godog
import (
"flag"
"fmt"
"regexp"
"github.com/DATA-DOG/godog/gherkin"
)
type BeforeScenarioHandler interface {
BeforeScenario(scenario *gherkin.Scenario)
}
type BeforeScenarioHandlerFunc func(scenario *gherkin.Scenario)
func (f BeforeScenarioHandlerFunc) BeforeScenario(scenario *gherkin.Scenario) {
f(scenario)
}
// Objects implementing the StepHandler interface can be
// registered as step definitions in godog
//
// HandleStep method receives all arguments which
// will be matched according to the regular expression
// which is passed with a step registration.
// The error in return - represents a reason of failure.
//
// Returning signals that the step has finished
// and that the feature runner can move on to the next
// step.
type StepHandler interface {
HandleStep(args ...Arg) error
}
// StepHandlerFunc type is an adapter to allow the use of
// ordinary functions as Step handlers. If f is a function
// with the appropriate signature, StepHandlerFunc(f) is a
// StepHandler object that calls f.
type StepHandlerFunc func(...Arg) error
// HandleStep calls f(step_arguments...).
func (f StepHandlerFunc) HandleStep(args ...Arg) error {
return f(args...)
}
var errPending = fmt.Errorf("pending step")
type stepMatchHandler struct {
handler StepHandler
expr *regexp.Regexp
}
// Suite is an interface which allows various contexts
// to register step definitions and event handlers
type Suite interface {
Step(expr *regexp.Regexp, h StepHandler)
BeforeScenario(h BeforeScenarioHandler)
}
type suite struct {
beforeScenarioHandlers []BeforeScenarioHandler
stepHandlers []*stepMatchHandler
features []*gherkin.Feature
fmt Formatter
}
// New initializes a suite which supports the Suite
// interface. The instance is passed around to all
// context initialization functions from *_test.go files
func New() *suite {
return &suite{}
}
// Step allows to register a StepHandler in Godog
// feature suite, the handler will be applied to all
// steps matching the given regexp
//
// Note that if there are two handlers which may match
// the same step, then the only first matched handler
// will be applied
//
// If none of the StepHandlers are matched, then a pending
// step error will be raised.
func (s *suite) Step(expr *regexp.Regexp, h StepHandler) {
s.stepHandlers = append(s.stepHandlers, &stepMatchHandler{
handler: h,
expr: expr,
})
}
func (s *suite) BeforeScenario(h BeforeScenarioHandler) {
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, h)
}
// Run - runs a godog feature suite
func (s *suite) Run() {
var err error
if !flag.Parsed() {
flag.Parse()
}
fatal(cfg.validate())
s.fmt = cfg.formatter()
s.features, err = cfg.features()
fatal(err)
for _, f := range s.features {
s.runFeature(f)
}
s.fmt.Summary()
}
func (s *suite) runStep(step *gherkin.Step) (err error) {
var match *stepMatchHandler
var args []Arg
for _, h := range s.stepHandlers {
if m := h.expr.FindStringSubmatch(step.Text); len(m) > 0 {
match = h
for _, a := range m[1:] {
args = append(args, Arg(a))
}
break
}
}
if match == nil {
s.fmt.Undefined(step)
return errPending
}
defer func() {
if e := recover(); e != nil {
err = e.(error)
s.fmt.Failed(step, match, err)
}
}()
if err = match.handler.HandleStep(args...); err != nil {
s.fmt.Failed(step, match, err)
} else {
s.fmt.Passed(step, match)
}
return
}
func (s *suite) runSteps(steps []*gherkin.Step) bool {
var failed bool
for _, step := range steps {
if failed {
s.fmt.Skipped(step)
continue
}
if err := s.runStep(step); err != nil {
failed = true
}
}
return failed
}
func (s *suite) skipSteps(steps []*gherkin.Step) {
for _, step := range steps {
s.fmt.Skipped(step)
}
}
func (s *suite) runFeature(f *gherkin.Feature) {
s.fmt.Node(f)
var failed bool
for _, scenario := range f.Scenarios {
// run before scenario handlers
for _, h := range s.beforeScenarioHandlers {
h.BeforeScenario(scenario)
}
// background
if f.Background != nil && !failed {
s.fmt.Node(f.Background)
failed = s.runSteps(f.Background.Steps)
}
// scenario
s.fmt.Node(scenario)
if failed {
s.skipSteps(scenario.Steps)
} else {
s.runSteps(scenario.Steps)
}
}
}