add pending step error and handling

Этот коммит содержится в:
gedi 2015-06-26 14:51:29 +03:00
родитель b3b02bc417
коммит 6a99e1320d
7 изменённых файлов: 179 добавлений и 25 удалений

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

@ -94,3 +94,72 @@ Feature: run features
""" """
I should have 1 scenario registered I should have 1 scenario registered
""" """
Scenario: should match undefined steps in a row
Given a feature "undefined.feature" file:
"""
Feature: undefined feature
Scenario: parse a scenario
Given undefined step
When undefined action
Then I should have 1 scenario registered
"""
When I run feature suite
Then the suite should have passed
And the following steps should be undefined:
"""
undefined step
undefined action
"""
And the following step should be skipped:
"""
I should have 1 scenario registered
"""
Scenario: should skip steps on pending
Given a feature "pending.feature" file:
"""
Feature: pending feature
Scenario: parse a scenario
Given undefined step
When pending step
Then I should have 1 scenario registered
"""
When I run feature suite
Then the suite should have passed
And the following step should be undefined:
"""
undefined step
"""
And the following step should be skipped:
"""
pending step
I should have 1 scenario registered
"""
Scenario: should handle pending step
Given a feature "pending.feature" file:
"""
Feature: pending feature
Scenario: parse a scenario
Given a feature path "features/load.feature:6"
When pending step
Then I should have 1 scenario registered
"""
When I run feature suite
Then the suite should have passed
And the following step should be passed:
"""
a feature path "features/load.feature:6"
"""
And the following step should be pending:
"""
pending step
"""
And the following step should be skipped:
"""
I should have 1 scenario registered
"""

12
fmt.go
Просмотреть файл

@ -39,6 +39,7 @@ type Formatter interface {
Passed(*gherkin.Step, *StepDef) Passed(*gherkin.Step, *StepDef)
Skipped(*gherkin.Step) Skipped(*gherkin.Step)
Undefined(*gherkin.Step) Undefined(*gherkin.Step)
Pending(*gherkin.Step, *StepDef)
Summary() Summary()
} }
@ -73,10 +74,19 @@ type skipped struct {
step *gherkin.Step step *gherkin.Step
} }
// undefined represents a pending step data structure // undefined represents an undefined step data structure
// with all necessary references // with all necessary references
type undefined struct { type undefined struct {
feature *feature feature *feature
owner interface{} owner interface{}
step *gherkin.Step step *gherkin.Step
} }
// pending represents a pending step data structure
// with all necessary references
type pending struct {
feature *feature
owner interface{}
step *gherkin.Step
def *StepDef
}

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

@ -27,7 +27,6 @@ type pretty struct {
backgroundSteps int backgroundSteps int
// outline // outline
outline *gherkin.ScenarioOutline
outlineSteps []interface{} outlineSteps []interface{}
outlineNumExample int outlineNumExample int
outlineNumExamples int outlineNumExamples int
@ -39,6 +38,7 @@ type pretty struct {
passed []*passed passed []*passed
skipped []*skipped skipped []*skipped
undefined []*undefined undefined []*undefined
pending []*pending
} }
// a line number representation in feature file // a line number representation in feature file
@ -95,7 +95,6 @@ func (f *pretty) Node(node interface{}) {
fmt.Println("\n" + text) fmt.Println("\n" + text)
case *gherkin.ScenarioOutline: case *gherkin.ScenarioOutline:
f.scope = t f.scope = t
f.outline = t
f.commentPos = f.longestStep(t.Steps, f.length(t)) f.commentPos = f.longestStep(t.Steps, f.length(t))
text := s(f.indent) + bcl(t.Keyword+": ", white) + t.Name text := s(f.indent) + bcl(t.Keyword+": ", white) + t.Name
text += s(f.commentPos-f.length(t)+1) + f.line(t.Location) text += s(f.commentPos-f.length(t)+1) + f.line(t.Location)
@ -136,7 +135,7 @@ func (f *pretty) Summary() {
fmt.Println(" " + cl(fail, red)) fmt.Println(" " + cl(fail, red))
} }
} }
var total, passed int var total, passed, undefined int
for _, ft := range f.features { for _, ft := range f.features {
for _, def := range ft.ScenarioDefinitions { for _, def := range ft.ScenarioDefinitions {
switch t := def.(type) { switch t := def.(type) {
@ -150,12 +149,22 @@ func (f *pretty) Summary() {
} }
} }
passed = total passed = total
var owner interface{}
for _, undef := range f.undefined {
if owner != undef.owner {
undefined++
owner = undef.owner
}
}
var steps, parts, scenarios []string var steps, parts, scenarios []string
nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending)
if len(f.passed) > 0 { if len(f.passed) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green)) steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green))
} }
if len(f.pending) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
}
if len(f.failed) > 0 { if len(f.failed) > 0 {
passed -= len(f.failed) passed -= len(f.failed)
parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red)) parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red))
@ -165,9 +174,9 @@ func (f *pretty) Summary() {
steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan)) steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan))
} }
if len(f.undefined) > 0 { if len(f.undefined) > 0 {
passed -= len(f.undefined) passed -= undefined
parts = append(parts, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow)) parts = append(parts, cl(fmt.Sprintf("%d undefined", undefined), yellow))
steps = append(steps, parts[len(parts)-1]) steps = append(steps, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow))
} }
if passed > 0 { if passed > 0 {
scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green)) scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green))
@ -324,6 +333,11 @@ func (f *pretty) stepDetails(stepAction interface{}) (owner interface{}, step *g
step = typ.step step = typ.step
owner = typ.owner owner = typ.owner
c = yellow c = yellow
case *pending:
step = typ.step
def = typ.def
owner = typ.owner
c = yellow
default: default:
fatal(fmt.Errorf("unexpected step type received: %T", typ)) fatal(fmt.Errorf("unexpected step type received: %T", typ))
} }
@ -358,6 +372,9 @@ func (f *pretty) printStepKind(stepAction interface{}) {
if err != nil { if err != nil {
fmt.Println(s(f.indent*2) + bcl(err, red)) fmt.Println(s(f.indent*2) + bcl(err, red))
} }
if _, ok := stepAction.(*pending); ok {
fmt.Println(s(f.indent*3) + cl("TODO: write pending definition", yellow))
}
} }
// print table with aligned table cells // print table with aligned table cells
@ -400,6 +417,12 @@ func (f *pretty) Failed(step *gherkin.Step, match *StepDef, err error) {
f.failed = append(f.failed, s) f.failed = append(f.failed, s)
} }
func (f *pretty) Pending(step *gherkin.Step, match *StepDef) {
s := &pending{owner: f.scope, feature: f.features[len(f.features)-1], step: step, def: match}
f.printStepKind(s)
f.pending = append(f.pending, s)
}
// longest gives a list of longest columns of all rows in Table // longest gives a list of longest columns of all rows in Table
func longest(tbl interface{}) []int { func longest(tbl interface{}) []int {
var rows []*gherkin.TableRow var rows []*gherkin.TableRow

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

@ -28,6 +28,7 @@ type progress struct {
passed []*passed passed []*passed
skipped []*skipped skipped []*skipped
undefined []*undefined undefined []*undefined
pending []*pending
} }
func (f *progress) Feature(ft *gherkin.Feature, p string) { func (f *progress) Feature(ft *gherkin.Feature, p string) {
@ -63,7 +64,7 @@ func (f *progress) Summary() {
fmt.Println(s(6) + cl("Error: ", red) + bcl(fail.err, red) + "\n") fmt.Println(s(6) + cl("Error: ", red) + bcl(fail.err, red) + "\n")
} }
} }
var total, passed int var total, passed, undefined int
for _, ft := range f.features { for _, ft := range f.features {
for _, def := range ft.ScenarioDefinitions { for _, def := range ft.ScenarioDefinitions {
switch t := def.(type) { switch t := def.(type) {
@ -77,12 +78,22 @@ func (f *progress) Summary() {
} }
} }
passed = total passed = total
var owner interface{}
for _, undef := range f.undefined {
if owner != undef.owner {
undefined++
owner = undef.owner
}
}
var steps, parts, scenarios []string var steps, parts, scenarios []string
nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending)
if len(f.passed) > 0 { if len(f.passed) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green)) steps = append(steps, cl(fmt.Sprintf("%d passed", len(f.passed)), green))
} }
if len(f.pending) > 0 {
steps = append(steps, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
}
if len(f.failed) > 0 { if len(f.failed) > 0 {
passed -= len(f.failed) passed -= len(f.failed)
parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red)) parts = append(parts, cl(fmt.Sprintf("%d failed", len(f.failed)), red))
@ -92,9 +103,9 @@ func (f *progress) Summary() {
steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan)) steps = append(steps, cl(fmt.Sprintf("%d skipped", len(f.skipped)), cyan))
} }
if len(f.undefined) > 0 { if len(f.undefined) > 0 {
passed -= len(f.undefined) passed -= undefined
parts = append(parts, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow)) parts = append(parts, cl(fmt.Sprintf("%d undefined", undefined), yellow))
steps = append(steps, parts[len(parts)-1]) steps = append(steps, cl(fmt.Sprintf("%d undefined", len(f.undefined)), yellow))
} }
if passed > 0 { if passed > 0 {
scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green)) scenarios = append(scenarios, cl(fmt.Sprintf("%d passed", passed), green))
@ -127,6 +138,8 @@ func (f *progress) step(step interface{}) {
fmt.Print(cl("F", red)) fmt.Print(cl("F", red))
case *undefined: case *undefined:
fmt.Print(cl("U", yellow)) fmt.Print(cl("U", yellow))
case *pending:
fmt.Print(cl("P", yellow))
} }
f.steps++ f.steps++
if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 { if math.Mod(float64(f.steps), float64(f.stepsPerRow)) == 0 {
@ -157,3 +170,9 @@ func (f *progress) Failed(step *gherkin.Step, match *StepDef, err error) {
f.failed = append(f.failed, s) f.failed = append(f.failed, s)
f.step(s) f.step(s)
} }
func (f *progress) Pending(step *gherkin.Step, match *StepDef) {
s := &pending{owner: f.owner, feature: f.features[len(f.features)-1], step: step, def: match}
f.pending = append(f.pending, s)
f.step(s)
}

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

@ -11,6 +11,7 @@ type testFormatter struct {
passed []*passed passed []*passed
skipped []*skipped skipped []*skipped
undefined []*undefined undefined []*undefined
pending []*pending
} }
func (f *testFormatter) Feature(ft *gherkin.Feature, p string) { func (f *testFormatter) Feature(ft *gherkin.Feature, p string) {
@ -47,3 +48,8 @@ func (f *testFormatter) Undefined(step *gherkin.Step) {
func (f *testFormatter) Failed(step *gherkin.Step, match *StepDef, err error) { func (f *testFormatter) Failed(step *gherkin.Step, match *StepDef, err error) {
f.failed = append(f.failed, &failed{owner: f.owner, feature: f.features[len(f.features)-1], step: step, def: match, err: err}) f.failed = append(f.failed, &failed{owner: f.owner, feature: f.features[len(f.features)-1], step: step, def: match, err: err})
} }
func (f *testFormatter) Pending(step *gherkin.Step, match *StepDef) {
s := &pending{owner: f.owner, feature: f.features[len(f.features)-1], step: step, def: match}
f.pending = append(f.pending, s)
}

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

@ -24,6 +24,10 @@ type feature struct {
// ErrUndefined is returned in case if step definition was not found // ErrUndefined is returned in case if step definition was not found
var ErrUndefined = fmt.Errorf("step is undefined") var ErrUndefined = fmt.Errorf("step is undefined")
// ErrPending should be returned by step definition if
// step implementation is pending
var ErrPending = fmt.Errorf("step implementation is pending")
// Suite is an interface which allows various contexts // Suite is an interface which allows various contexts
// to register steps and event handlers. // to register steps and event handlers.
// //
@ -282,13 +286,18 @@ func (s *suite) matchStep(step *gherkin.Step) *StepDef {
return nil return nil
} }
func (s *suite) runStep(step *gherkin.Step) (err error) { func (s *suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
match := s.matchStep(step) match := s.matchStep(step)
if match == nil { if match == nil {
s.fmt.Undefined(step) s.fmt.Undefined(step)
return ErrUndefined return ErrUndefined
} }
if prevStepErr != nil {
s.fmt.Skipped(step)
return nil
}
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
err, ok := e.(error) err, ok := e.(error)
@ -299,27 +308,35 @@ func (s *suite) runStep(step *gherkin.Step) (err error) {
} }
}() }()
if err = match.run(); err != nil { err = match.run()
s.fmt.Failed(step, match, err) switch err {
} else { case nil:
s.fmt.Passed(step, match) s.fmt.Passed(step, match)
case ErrPending:
s.fmt.Pending(step, match)
default:
s.fmt.Failed(step, match, err)
} }
return return
} }
func (s *suite) runSteps(steps []*gherkin.Step) (err error) { func (s *suite) runSteps(steps []*gherkin.Step) (err error) {
for _, step := range steps { for _, step := range steps {
if err != nil {
s.fmt.Skipped(step)
continue
}
// run before step handlers // run before step handlers
for _, f := range s.beforeStepHandlers { for _, f := range s.beforeStepHandlers {
f(step) f(step)
} }
err = s.runStep(step) stepErr := s.runStep(step, err)
switch stepErr {
case ErrUndefined:
err = stepErr
case ErrPending:
err = stepErr
case nil:
default:
err = stepErr
}
// run after step handlers // run after step handlers
for _, f := range s.afterStepHandlers { for _, f := range s.afterStepHandlers {
@ -401,7 +418,7 @@ func (s *suite) runFeature(f *feature) {
case *gherkin.Scenario: case *gherkin.Scenario:
err = s.runScenario(t, f.Background) err = s.runScenario(t, f.Background)
} }
if err != nil && err != ErrUndefined { if err != nil && err != ErrUndefined && err != ErrPending {
s.failed = true s.failed = true
if s.stopOnFailure { if s.stopOnFailure {
return return
@ -424,6 +441,8 @@ func (s *suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (
// scenario // scenario
s.fmt.Node(scenario) s.fmt.Node(scenario)
switch err { switch err {
case ErrPending:
s.skipSteps(scenario.Steps)
case ErrUndefined: case ErrUndefined:
s.skipSteps(scenario.Steps) s.skipSteps(scenario.Steps)
case nil: case nil:

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

@ -28,12 +28,16 @@ func SuiteContext(s Suite) {
s.Step(`^a failing step`, c.aFailingStep) s.Step(`^a failing step`, c.aFailingStep)
s.Step(`^this step should fail`, c.aFailingStep) s.Step(`^this step should fail`, c.aFailingStep)
s.Step(`^the following steps? should be (passed|failed|skipped|undefined):`, c.followingStepsShouldHave) s.Step(`^the following steps? should be (passed|failed|skipped|undefined|pending):`, c.followingStepsShouldHave)
// lt // lt
s.Step(`^savybių aplankas "([^"]*)"$`, c.featurePath) s.Step(`^savybių aplankas "([^"]*)"$`, c.featurePath)
s.Step(`^aš išskaitau savybes$`, c.parseFeatures) s.Step(`^aš išskaitau savybes$`, c.parseFeatures)
s.Step(`^aš turėčiau turėti ([\d]+) savybių failus:$`, c.iShouldHaveNumFeatureFiles) s.Step(`^aš turėčiau turėti ([\d]+) savybių failus:$`, c.iShouldHaveNumFeatureFiles)
s.Step(`^pending step$`, func(...*Arg) error {
return ErrPending
})
} }
type firedEvent struct { type firedEvent struct {
@ -79,6 +83,10 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *gherkin.Do
for _, st := range s.fmt.undefined { for _, st := range s.fmt.undefined {
actual = append(actual, st.step.Text) actual = append(actual, st.step.Text)
} }
case "pending":
for _, st := range s.fmt.pending {
actual = append(actual, st.step.Text)
}
default: default:
return fmt.Errorf("unexpected step status wanted: %s", status) return fmt.Errorf("unexpected step status wanted: %s", status)
} }