instead of Suite interface expose Suite struct
* 570d70a update examples regarding Suite interface removal, closes #11
Этот коммит содержится в:
родитель
46f5218d36
коммит
ca36316b7a
19 изменённых файлов: 142 добавлений и 234 удалений
38
README.md
38
README.md
|
@ -25,11 +25,15 @@ not changed most likely. I'll try to respect **backward compatibility** as much
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
The following example can be [found here](https://github.com/DATA-DOG/godog/tree/master/examples/godogs).
|
||||||
|
|
||||||
|
#### Step 1
|
||||||
|
|
||||||
Imagine we have a **godog cart** to serve godogs for dinner. At first, we describe our feature
|
Imagine we have a **godog cart** to serve godogs for dinner. At first, we describe our feature
|
||||||
in plain text:
|
in plain text:
|
||||||
|
|
||||||
``` gherkin
|
``` gherkin
|
||||||
# file: /tmp/godog/godog.feature
|
# file: examples/godogs/godog.feature
|
||||||
Feature: eat godogs
|
Feature: eat godogs
|
||||||
In order to be happy
|
In order to be happy
|
||||||
As a hungry gopher
|
As a hungry gopher
|
||||||
|
@ -44,22 +48,27 @@ Feature: eat godogs
|
||||||
As a developer, your work is done as soon as you’ve made the program behave as
|
As a developer, your work is done as soon as you’ve made the program behave as
|
||||||
described in the Scenario.
|
described in the Scenario.
|
||||||
|
|
||||||
If you run `godog godog.feature` inside the **/tmp/godog** directory.
|
#### Step 2
|
||||||
|
|
||||||
|
If you run `godog godog.feature` inside the **examples/godogs** directory.
|
||||||
You should see that the steps are undefined:
|
You should see that the steps are undefined:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It gives you undefined step snippets to implement in your test context. You may copy these snippets
|
It gives you undefined step snippets to implement in your test context. You may copy these snippets
|
||||||
into **godog_test.go** file.
|
into your `*_test.go` file.
|
||||||
|
|
||||||
Now if you run the tests again. You should see that the definition is now pending. You may change
|
Now if you run the tests again. You should see that the definition is now pending. You may change
|
||||||
**ErrPending** to **nil** and the scenario will pass successfully.
|
**ErrPending** to **nil** and the scenario will pass successfully.
|
||||||
|
|
||||||
Since we need a working implementation, we may start by implementing what is necessary.
|
Since we need a working implementation, we may start by implementing only what is necessary.
|
||||||
We only need a number of **godogs** for now.
|
|
||||||
|
#### Step 3
|
||||||
|
|
||||||
|
We only need a number of **godogs** for now. Lets define steps.
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
/* file: /tmp/godog/godog.go */
|
/* file: examples/godogs/godog.go */
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var Godogs int
|
var Godogs int
|
||||||
|
@ -67,10 +76,12 @@ var Godogs int
|
||||||
func main() { /* usual main func */ }
|
func main() { /* usual main func */ }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Step 4
|
||||||
|
|
||||||
Now lets finish our step implementations in order to test our feature requirements:
|
Now lets finish our step implementations in order to test our feature requirements:
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
/* file: /tmp/godog/godog_test.go */
|
/* file: examples/godogs/godog_test.go */
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -99,7 +110,7 @@ func thereShouldBeRemaining(remaining int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureContext(s godog.Suite) {
|
func featureContext(s *godog.Suite) {
|
||||||
s.Step(`^there are (\d+) godogs$`, thereAreGodogs)
|
s.Step(`^there are (\d+) godogs$`, thereAreGodogs)
|
||||||
s.Step(`^I eat (\d+)$`, iEat)
|
s.Step(`^I eat (\d+)$`, iEat)
|
||||||
s.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
|
s.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
|
||||||
|
@ -126,8 +137,15 @@ See `godog -h` for general command options.
|
||||||
|
|
||||||
See implementation examples:
|
See implementation examples:
|
||||||
|
|
||||||
- [rest API server](https://github.com/DATA-DOG/godog/tree/master/examples/api) implementation and tests
|
- [rest API server](https://github.com/DATA-DOG/godog/tree/master/examples/api)
|
||||||
- [ls command](https://github.com/DATA-DOG/godog/tree/master/examples/ls) implementation and tests
|
- [godogs](https://github.com/DATA-DOG/godog/tree/master/examples/godogs)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
**2015-07-03**
|
||||||
|
- changed **godog.Suite** from interface to struct. Context registration should be updated accordingly. The reason
|
||||||
|
for change: since it exports the same methods and there is no need to mock a function in tests, there is no
|
||||||
|
obvious reason to keep an interface.
|
||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
|
|
19
builder.go
19
builder.go
|
@ -82,7 +82,7 @@ func (b *builder) parseFile(path string) error {
|
||||||
b.Internal = true
|
b.Internal = true
|
||||||
}
|
}
|
||||||
b.deleteMainFunc(f)
|
b.deleteMainFunc(f)
|
||||||
b.registerSteps(f)
|
b.registerContexts(f)
|
||||||
b.deleteImports(f)
|
b.deleteImports(f)
|
||||||
b.files[path] = f
|
b.files[path] = f
|
||||||
|
|
||||||
|
@ -122,22 +122,25 @@ func (b *builder) deleteMainFunc(f *ast.File) {
|
||||||
f.Decls = decls
|
f.Decls = decls
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) registerSteps(f *ast.File) {
|
func (b *builder) registerContexts(f *ast.File) {
|
||||||
for _, d := range f.Decls {
|
for _, d := range f.Decls {
|
||||||
switch fun := d.(type) {
|
switch fun := d.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
for _, param := range fun.Type.Params.List {
|
for _, param := range fun.Type.Params.List {
|
||||||
switch expr := param.Type.(type) {
|
switch expr := param.Type.(type) {
|
||||||
case *ast.SelectorExpr:
|
case *ast.StarExpr:
|
||||||
switch x := expr.X.(type) {
|
switch x := expr.X.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
if x.Name == "godog" && expr.Sel.Name == "Suite" {
|
if x.Name == "Suite" {
|
||||||
b.Contexts = append(b.Contexts, fun.Name.Name)
|
b.Contexts = append(b.Contexts, fun.Name.Name)
|
||||||
}
|
}
|
||||||
}
|
case *ast.SelectorExpr:
|
||||||
case *ast.Ident:
|
switch t := x.X.(type) {
|
||||||
if expr.Name == "Suite" {
|
case *ast.Ident:
|
||||||
b.Contexts = append(b.Contexts, fun.Name.Name)
|
if t.Name == "godog" && x.Sel.Name == "Suite" {
|
||||||
|
b.Contexts = append(b.Contexts, fun.Name.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) error {
|
||||||
return godog.ErrPending
|
return godog.ErrPending
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureContext(s godog.Suite) {
|
func featureContext(s *godog.Suite) {
|
||||||
api := &apiFeature{}
|
api := &apiFeature{}
|
||||||
s.Step(`^I send "([^"]*)" request to "([^"]*)"$`, api.iSendrequestTo)
|
s.Step(`^I send "([^"]*)" request to "([^"]*)"$`, api.iSendrequestTo)
|
||||||
s.Step(`^the response code should be (\d+)$`, api.theResponseCodeShouldBe)
|
s.Step(`^the response code should be (\d+)$`, api.theResponseCodeShouldBe)
|
||||||
|
@ -158,7 +158,7 @@ func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) (err er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureContext(s godog.Suite) {
|
func featureContext(s *godog.Suite) {
|
||||||
api := &apiFeature{}
|
api := &apiFeature{}
|
||||||
|
|
||||||
s.BeforeScenario(api.resetResponse)
|
s.BeforeScenario(api.resetResponse)
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) (err er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureContext(s godog.Suite) {
|
func featureContext(s *godog.Suite) {
|
||||||
api := &apiFeature{}
|
api := &apiFeature{}
|
||||||
|
|
||||||
s.BeforeScenario(api.resetResponse)
|
s.BeforeScenario(api.resetResponse)
|
||||||
|
|
Двоичные данные
examples/api/screenshots/undefined.png
Двоичные данные
examples/api/screenshots/undefined.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 151 КиБ После Ширина: | Высота: | Размер: 148 КиБ |
|
@ -117,7 +117,7 @@ func (a *apiFeature) thereAreUsers(users *gherkin.DataTable) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func featureContext(s godog.Suite) {
|
func featureContext(s *godog.Suite) {
|
||||||
api := &apiFeature{}
|
api := &apiFeature{}
|
||||||
|
|
||||||
s.BeforeScenario(api.resetResponse)
|
s.BeforeScenario(api.resetResponse)
|
||||||
|
|
9
examples/godogs/godog.feature
Обычный файл
9
examples/godogs/godog.feature
Обычный файл
|
@ -0,0 +1,9 @@
|
||||||
|
Feature: eat godogs
|
||||||
|
In order to be happy
|
||||||
|
As a hungry gopher
|
||||||
|
I need to be able to eat godogs
|
||||||
|
|
||||||
|
Scenario: Eat 5 out of 12
|
||||||
|
Given there are 12 godogs
|
||||||
|
When I eat 5
|
||||||
|
Then there should be 7 remaining
|
5
examples/godogs/godog.go
Обычный файл
5
examples/godogs/godog.go
Обычный файл
|
@ -0,0 +1,5 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
var Godogs int
|
||||||
|
|
||||||
|
func main() { /* usual main func */ }
|
37
examples/godogs/godog_test.go
Обычный файл
37
examples/godogs/godog_test.go
Обычный файл
|
@ -0,0 +1,37 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/godog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func thereAreGodogs(available int) error {
|
||||||
|
Godogs = available
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iEat(num int) error {
|
||||||
|
if Godogs < num {
|
||||||
|
return fmt.Errorf("you cannot eat %d godogs, there are %d available", num, Godogs)
|
||||||
|
}
|
||||||
|
Godogs -= num
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func thereShouldBeRemaining(remaining int) error {
|
||||||
|
if Godogs != remaining {
|
||||||
|
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, Godogs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func featureContext(s *godog.Suite) {
|
||||||
|
s.Step(`^there are (\d+) godogs$`, thereAreGodogs)
|
||||||
|
s.Step(`^I eat (\d+)$`, iEat)
|
||||||
|
s.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
|
||||||
|
|
||||||
|
s.BeforeScenario(func(interface{}) {
|
||||||
|
Godogs = 0 // clean the state before every scenario
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
# ls feature
|
|
||||||
|
|
||||||
In order to test our **ls** feature with **Godog**, run:
|
|
||||||
|
|
||||||
go get github.com/DATA-DOG/godog/cmd/godog
|
|
||||||
$GOPATH/bin/godog ls.feature
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
Feature: ls
|
|
||||||
In order to see the directory structure
|
|
||||||
As a UNIX user
|
|
||||||
I need to be able to list directory contents
|
|
||||||
|
|
||||||
Background:
|
|
||||||
Given I am in a directory "test"
|
|
||||||
|
|
||||||
Scenario: lists files in directory
|
|
||||||
Given I have a file named "foo"
|
|
||||||
And I have a file named "bar"
|
|
||||||
When I run ls
|
|
||||||
Then I should get output:
|
|
||||||
"""
|
|
||||||
bar
|
|
||||||
foo
|
|
||||||
"""
|
|
||||||
|
|
||||||
Scenario: lists files and directories
|
|
||||||
Given I have a file named "foo"
|
|
||||||
And I have a directory named "dir"
|
|
||||||
When I run ls
|
|
||||||
Then I should get output:
|
|
||||||
"""
|
|
||||||
dir
|
|
||||||
foo
|
|
||||||
"""
|
|
|
@ -1,35 +0,0 @@
|
||||||
// Example - demonstrates ls command implementation tests.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var location string
|
|
||||||
switch {
|
|
||||||
case os.Args[1] != "":
|
|
||||||
location = os.Args[1]
|
|
||||||
default:
|
|
||||||
location = "."
|
|
||||||
}
|
|
||||||
if err := ls(location, os.Stdout); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ls(path string, w io.Writer) error {
|
|
||||||
return filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
|
|
||||||
switch {
|
|
||||||
case f.IsDir() && f.Name() != "." && f.Name() != ".." && filepath.Base(path) != f.Name():
|
|
||||||
w.Write([]byte(f.Name() + "\n"))
|
|
||||||
return filepath.SkipDir
|
|
||||||
case !f.IsDir():
|
|
||||||
w.Write([]byte(f.Name() + "\n"))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/DATA-DOG/godog"
|
|
||||||
"github.com/cucumber/gherkin-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
type lsFeature struct {
|
|
||||||
dir string
|
|
||||||
buf *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func lsFeatureContext(s godog.Suite) {
|
|
||||||
c := &lsFeature{buf: bytes.NewBuffer(make([]byte, 1024))}
|
|
||||||
|
|
||||||
s.Step(`^I am in a directory "([^"]*)"$`, c.iAmInDirectory)
|
|
||||||
s.Step(`^I have a (file|directory) named "([^"]*)"$`, c.iHaveFileOrDirectoryNamed)
|
|
||||||
s.Step(`^I run ls$`, c.iRunLs)
|
|
||||||
s.Step(`^I should get output:$`, c.iShouldGetOutput)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *lsFeature) iAmInDirectory(name string) error {
|
|
||||||
f.dir = os.TempDir() + "/" + name
|
|
||||||
if err := os.RemoveAll(f.dir); err != nil && !os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Mkdir(f.dir, 0775)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *lsFeature) iHaveFileOrDirectoryNamed(typ, name string) (err error) {
|
|
||||||
if len(f.dir) == 0 {
|
|
||||||
return fmt.Errorf("the directory was not chosen yet")
|
|
||||||
}
|
|
||||||
switch typ {
|
|
||||||
case "file":
|
|
||||||
err = ioutil.WriteFile(f.dir+"/"+name, []byte{}, 0664)
|
|
||||||
case "directory":
|
|
||||||
err = os.Mkdir(f.dir+"/"+name, 0775)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *lsFeature) iShouldGetOutput(names *gherkin.DocString) error {
|
|
||||||
expected := strings.Split(names.Content, "\n")
|
|
||||||
actual := strings.Split(strings.TrimSpace(f.buf.String()), "\n")
|
|
||||||
if len(expected) != len(actual) {
|
|
||||||
return fmt.Errorf("number of expected output lines %d, does not match actual: %d", len(expected), len(actual))
|
|
||||||
}
|
|
||||||
for i, line := range actual {
|
|
||||||
if line != expected[i] {
|
|
||||||
return fmt.Errorf(`expected line "%s" at position: %d to match "%s", but it did not`, expected[i], i, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *lsFeature) iRunLs() error {
|
|
||||||
f.buf.Reset()
|
|
||||||
return ls(f.dir, f.buf)
|
|
||||||
}
|
|
4
flags.go
4
flags.go
|
@ -5,7 +5,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func flags(s *suite) *flag.FlagSet {
|
// Flags builds a *flag.FlagSet with all flags
|
||||||
|
// required for the godog suite
|
||||||
|
func flags(s *Suite) *flag.FlagSet {
|
||||||
set := flag.NewFlagSet("godog", flag.ExitOnError)
|
set := flag.NewFlagSet("godog", flag.ExitOnError)
|
||||||
set.StringVar(&s.format, "format", "pretty", "")
|
set.StringVar(&s.format, "format", "pretty", "")
|
||||||
set.StringVar(&s.format, "f", "pretty", "")
|
set.StringVar(&s.format, "f", "pretty", "")
|
||||||
|
|
2
fmt.go
2
fmt.go
|
@ -30,7 +30,7 @@ var undefinedSnippetsTpl = template.Must(template.New("snippets").Funcs(snippetH
|
||||||
return godog.ErrPending
|
return godog.ErrPending
|
||||||
}
|
}
|
||||||
|
|
||||||
{{end}}func featureContext(s godog.Suite) { {{ range . }}
|
{{end}}func featureContext(s *godog.Suite) { {{ range . }}
|
||||||
s.Step({{ backticked .Expr }}, {{ .Method }}){{end}}
|
s.Step({{ backticked .Expr }}, {{ .Method }}){{end}}
|
||||||
}
|
}
|
||||||
`))
|
`))
|
||||||
|
|
Двоичные данные
screenshots/undefined.png
Двоичные данные
screenshots/undefined.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 96 КиБ После Ширина: | Высота: | Размер: 109 КиБ |
109
suite.go
109
suite.go
|
@ -28,52 +28,18 @@ var ErrUndefined = fmt.Errorf("step is undefined")
|
||||||
// step implementation is pending
|
// step implementation is pending
|
||||||
var ErrPending = fmt.Errorf("step implementation is pending")
|
var ErrPending = fmt.Errorf("step implementation is pending")
|
||||||
|
|
||||||
// Suite is an interface which allows various contexts
|
// Suite allows various contexts
|
||||||
// to register steps and event handlers.
|
// to register steps and event handlers.
|
||||||
//
|
//
|
||||||
// When running a test suite, this interface is passed
|
// When running a test suite, the instance of Suite
|
||||||
// to all functions (contexts), which have it as a
|
// is passed to all functions (contexts), which
|
||||||
// first and only argument.
|
// have it as a first and only argument.
|
||||||
//
|
//
|
||||||
// Note that all event hooks does not catch panic errors
|
// Note that all event hooks does not catch panic errors
|
||||||
// in order to have a trace information. Only step
|
// in order to have a trace information. Only step
|
||||||
// executions are catching panic error since it may
|
// executions are catching panic error since it may
|
||||||
// be a context specific error.
|
// be a context specific error.
|
||||||
type Suite interface {
|
type Suite struct {
|
||||||
// Run the test suite
|
|
||||||
Run()
|
|
||||||
|
|
||||||
// Registers a step which will execute stepFunc
|
|
||||||
// on step expr match
|
|
||||||
//
|
|
||||||
// expr can be either a string or a *regexp.Regexp
|
|
||||||
// stepFunc is a func to handle the step, arguments
|
|
||||||
// are set from matched step
|
|
||||||
Step(expr interface{}, h interface{})
|
|
||||||
|
|
||||||
// BeforeSuite registers a func to run on initial
|
|
||||||
// suite startup
|
|
||||||
BeforeSuite(f func())
|
|
||||||
|
|
||||||
// BeforeScenario registers a func to run before
|
|
||||||
// every *gherkin.Scenario or *gherkin.ScenarioOutline
|
|
||||||
BeforeScenario(f func(interface{}))
|
|
||||||
|
|
||||||
// BeforeStep register a handler before every step
|
|
||||||
BeforeStep(f func(*gherkin.Step))
|
|
||||||
|
|
||||||
// AfterStep register a handler after every step
|
|
||||||
AfterStep(f func(*gherkin.Step, error))
|
|
||||||
|
|
||||||
// AfterScenario registers a func to run after
|
|
||||||
// every *gherkin.Scenario or *gherkin.ScenarioOutline
|
|
||||||
AfterScenario(f func(interface{}, error))
|
|
||||||
|
|
||||||
// AfterSuite runs func int the end of tests
|
|
||||||
AfterSuite(f func())
|
|
||||||
}
|
|
||||||
|
|
||||||
type suite struct {
|
|
||||||
steps []*StepDef
|
steps []*StepDef
|
||||||
features []*feature
|
features []*feature
|
||||||
fmt Formatter
|
fmt Formatter
|
||||||
|
@ -98,24 +64,27 @@ type suite struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a Suite. The instance is passed around
|
// New initializes a Suite. The instance is passed around
|
||||||
// to all context initialization functions from *_test.go files
|
// to all context initialization functions from *_test.go files.
|
||||||
func New() Suite {
|
func New() *Suite {
|
||||||
return &suite{}
|
return &Suite{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step allows to register a StepHandler in Godog
|
// Step allows to register a *StepDef in Godog
|
||||||
// feature suite, the handler will be applied to all
|
// feature suite, the definition will be applied
|
||||||
// steps matching the given Regexp expr
|
// to all steps matching the given Regexp expr.
|
||||||
//
|
//
|
||||||
// It will panic if expr is not a valid regular expression
|
// It will panic if expr is not a valid regular
|
||||||
|
// expression or stepFunc is not a valid step
|
||||||
|
// handler.
|
||||||
//
|
//
|
||||||
// Note that if there are two handlers which may match
|
// Note that if there are two definitions which may match
|
||||||
// the same step, then the only first matched handler
|
// the same step, then only the first matched handler
|
||||||
// will be applied.
|
// will be applied.
|
||||||
//
|
//
|
||||||
// If none of the StepHandlers are matched, then
|
// If none of the *StepDef is matched, then
|
||||||
// ErrUndefined error will be returned.
|
// ErrUndefined error will be returned when
|
||||||
func (s *suite) Step(expr interface{}, stepFunc interface{}) {
|
// running steps.
|
||||||
|
func (s *Suite) Step(expr interface{}, stepFunc interface{}) {
|
||||||
var regex *regexp.Regexp
|
var regex *regexp.Regexp
|
||||||
|
|
||||||
switch t := expr.(type) {
|
switch t := expr.(type) {
|
||||||
|
@ -152,7 +121,7 @@ func (s *suite) Step(expr interface{}, stepFunc interface{}) {
|
||||||
//
|
//
|
||||||
// Use it to prepare the test suite for a spin.
|
// Use it to prepare the test suite for a spin.
|
||||||
// Connect and prepare database for instance...
|
// Connect and prepare database for instance...
|
||||||
func (s *suite) BeforeSuite(f func()) {
|
func (s *Suite) BeforeSuite(f func()) {
|
||||||
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, f)
|
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,13 +134,13 @@ func (s *suite) BeforeSuite(f func()) {
|
||||||
// It is a good practice to restore the default state
|
// It is a good practice to restore the default state
|
||||||
// before every scenario so it would be isolated from
|
// before every scenario so it would be isolated from
|
||||||
// any kind of state.
|
// any kind of state.
|
||||||
func (s *suite) BeforeScenario(f func(interface{})) {
|
func (s *Suite) BeforeScenario(f func(interface{})) {
|
||||||
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, f)
|
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeStep registers a function or method
|
// BeforeStep registers a function or method
|
||||||
// to be run before every scenario
|
// to be run before every scenario
|
||||||
func (s *suite) BeforeStep(f func(*gherkin.Step)) {
|
func (s *Suite) BeforeStep(f func(*gherkin.Step)) {
|
||||||
s.beforeStepHandlers = append(s.beforeStepHandlers, f)
|
s.beforeStepHandlers = append(s.beforeStepHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +153,7 @@ func (s *suite) BeforeStep(f func(*gherkin.Step)) {
|
||||||
//
|
//
|
||||||
// In some cases, for example when running a headless
|
// In some cases, for example when running a headless
|
||||||
// browser, to take a screenshot after failure.
|
// browser, to take a screenshot after failure.
|
||||||
func (s *suite) AfterStep(f func(*gherkin.Step, error)) {
|
func (s *Suite) AfterStep(f func(*gherkin.Step, error)) {
|
||||||
s.afterStepHandlers = append(s.afterStepHandlers, f)
|
s.afterStepHandlers = append(s.afterStepHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,18 +162,18 @@ func (s *suite) AfterStep(f func(*gherkin.Step, error)) {
|
||||||
//
|
//
|
||||||
// The interface argument may be *gherkin.Scenario
|
// The interface argument may be *gherkin.Scenario
|
||||||
// or *gherkin.ScenarioOutline
|
// or *gherkin.ScenarioOutline
|
||||||
func (s *suite) AfterScenario(f func(interface{}, error)) {
|
func (s *Suite) AfterScenario(f func(interface{}, error)) {
|
||||||
s.afterScenarioHandlers = append(s.afterScenarioHandlers, f)
|
s.afterScenarioHandlers = append(s.afterScenarioHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterSuite registers a function or method
|
// AfterSuite registers a function or method
|
||||||
// to be run once after suite runner
|
// to be run once after suite runner
|
||||||
func (s *suite) AfterSuite(f func()) {
|
func (s *Suite) AfterSuite(f func()) {
|
||||||
s.afterSuiteHandlers = append(s.afterSuiteHandlers, f)
|
s.afterSuiteHandlers = append(s.afterSuiteHandlers, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the Godog feature suite
|
// Run starts the Godog feature suite
|
||||||
func (s *suite) Run() {
|
func (s *Suite) Run() {
|
||||||
flagSet := flags(s)
|
flagSet := flags(s)
|
||||||
fatal(flagSet.Parse(os.Args[1:]))
|
fatal(flagSet.Parse(os.Args[1:]))
|
||||||
|
|
||||||
|
@ -249,7 +218,7 @@ func (s *suite) Run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) run() {
|
func (s *Suite) run() {
|
||||||
// run before suite handlers
|
// run before suite handlers
|
||||||
for _, f := range s.beforeSuiteHandlers {
|
for _, f := range s.beforeSuiteHandlers {
|
||||||
f()
|
f()
|
||||||
|
@ -269,7 +238,7 @@ func (s *suite) run() {
|
||||||
s.fmt.Summary()
|
s.fmt.Summary()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) matchStep(step *gherkin.Step) *StepDef {
|
func (s *Suite) matchStep(step *gherkin.Step) *StepDef {
|
||||||
for _, h := range s.steps {
|
for _, h := range s.steps {
|
||||||
if m := h.Expr.FindStringSubmatch(step.Text); len(m) > 0 {
|
if m := h.Expr.FindStringSubmatch(step.Text); len(m) > 0 {
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
@ -286,7 +255,7 @@ func (s *suite) matchStep(step *gherkin.Step) *StepDef {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) runStep(step *gherkin.Step, prevStepErr error) (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)
|
||||||
|
@ -330,7 +299,7 @@ func (s *suite) runStep(step *gherkin.Step, prevStepErr error) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) runSteps(steps []*gherkin.Step, prevErr error) (err error) {
|
func (s *Suite) runSteps(steps []*gherkin.Step, prevErr error) (err error) {
|
||||||
err = prevErr
|
err = prevErr
|
||||||
for _, step := range steps {
|
for _, step := range steps {
|
||||||
stepErr := s.runStep(step, err)
|
stepErr := s.runStep(step, err)
|
||||||
|
@ -347,13 +316,13 @@ func (s *suite) runSteps(steps []*gherkin.Step, prevErr error) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) skipSteps(steps []*gherkin.Step) {
|
func (s *Suite) skipSteps(steps []*gherkin.Step) {
|
||||||
for _, step := range steps {
|
for _, step := range steps {
|
||||||
s.fmt.Skipped(step)
|
s.fmt.Skipped(step)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (failErr error) {
|
func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (failErr error) {
|
||||||
s.fmt.Node(outline)
|
s.fmt.Node(outline)
|
||||||
|
|
||||||
for _, example := range outline.Examples {
|
for _, example := range outline.Examples {
|
||||||
|
@ -403,7 +372,7 @@ func (s *suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Backgrou
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) runFeature(f *feature) {
|
func (s *Suite) runFeature(f *feature) {
|
||||||
s.fmt.Feature(f.Feature, f.Path)
|
s.fmt.Feature(f.Feature, f.Path)
|
||||||
for _, scenario := range f.ScenarioDefinitions {
|
for _, scenario := range f.ScenarioDefinitions {
|
||||||
var err error
|
var err error
|
||||||
|
@ -425,7 +394,7 @@ func (s *suite) runFeature(f *feature) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (err error) {
|
func (s *Suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (err error) {
|
||||||
// run before scenario handlers
|
// run before scenario handlers
|
||||||
for _, f := range s.beforeScenarioHandlers {
|
for _, f := range s.beforeScenarioHandlers {
|
||||||
f(scenario)
|
f(scenario)
|
||||||
|
@ -448,7 +417,7 @@ func (s *suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) printStepDefinitions() {
|
func (s *Suite) printStepDefinitions() {
|
||||||
var longest int
|
var longest int
|
||||||
for _, def := range s.steps {
|
for _, def := range s.steps {
|
||||||
if longest < len(def.Expr.String()) {
|
if longest < len(def.Expr.String()) {
|
||||||
|
@ -465,7 +434,7 @@ func (s *suite) printStepDefinitions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) parseFeatures() (err error) {
|
func (s *Suite) parseFeatures() (err error) {
|
||||||
for _, pat := range s.paths {
|
for _, pat := range s.paths {
|
||||||
// check if line number is specified
|
// check if line number is specified
|
||||||
parts := strings.Split(pat, ":")
|
parts := strings.Split(pat, ":")
|
||||||
|
@ -525,7 +494,7 @@ func (s *suite) parseFeatures() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *suite) applyTagFilter(ft *gherkin.Feature) {
|
func (s *Suite) applyTagFilter(ft *gherkin.Feature) {
|
||||||
if len(s.tags) == 0 {
|
if len(s.tags) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -585,7 +554,7 @@ func hasTag(tags []string, tag string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on http://behat.readthedocs.org/en/v2.5/guides/6.cli.html#gherkin-filters
|
// based on http://behat.readthedocs.org/en/v2.5/guides/6.cli.html#gherkin-filters
|
||||||
func (s *suite) matchesTags(tags []string) (ok bool) {
|
func (s *Suite) matchesTags(tags []string) (ok bool) {
|
||||||
ok = true
|
ok = true
|
||||||
for _, andTags := range strings.Split(s.tags, "&&") {
|
for _, andTags := range strings.Split(s.tags, "&&") {
|
||||||
var okComma bool
|
var okComma bool
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/cucumber/gherkin-go"
|
"github.com/cucumber/gherkin-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SuiteContext(s Suite) {
|
func SuiteContext(s *Suite) {
|
||||||
c := &suiteContext{}
|
c := &suiteContext{}
|
||||||
|
|
||||||
s.BeforeScenario(c.ResetBeforeEachScenario)
|
s.BeforeScenario(c.ResetBeforeEachScenario)
|
||||||
|
@ -50,7 +50,7 @@ type firedEvent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type suiteContext struct {
|
type suiteContext struct {
|
||||||
testedSuite *suite
|
testedSuite *Suite
|
||||||
events []*firedEvent
|
events []*firedEvent
|
||||||
fmt *testFormatter
|
fmt *testFormatter
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ type suiteContext struct {
|
||||||
func (s *suiteContext) ResetBeforeEachScenario(interface{}) {
|
func (s *suiteContext) ResetBeforeEachScenario(interface{}) {
|
||||||
// reset whole suite with the state
|
// reset whole suite with the state
|
||||||
s.fmt = &testFormatter{}
|
s.fmt = &testFormatter{}
|
||||||
s.testedSuite = &suite{fmt: s.fmt}
|
s.testedSuite = &Suite{fmt: s.fmt}
|
||||||
// our tested suite will have the same context registered
|
// our tested suite will have the same context registered
|
||||||
SuiteContext(s.testedSuite)
|
SuiteContext(s.testedSuite)
|
||||||
// reset all fired events
|
// reset all fired events
|
||||||
|
|
|
@ -5,14 +5,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func assertNotMatchesTagFilter(tags []string, filter string, t *testing.T) {
|
func assertNotMatchesTagFilter(tags []string, filter string, t *testing.T) {
|
||||||
s := &suite{tags: filter}
|
s := &Suite{tags: filter}
|
||||||
if s.matchesTags(tags) {
|
if s.matchesTags(tags) {
|
||||||
t.Errorf(`expected tags: %v not to match tag filter "%s", but it did`, tags, filter)
|
t.Errorf(`expected tags: %v not to match tag filter "%s", but it did`, tags, filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertMatchesTagFilter(tags []string, filter string, t *testing.T) {
|
func assertMatchesTagFilter(tags []string, filter string, t *testing.T) {
|
||||||
s := &suite{tags: filter}
|
s := &Suite{tags: filter}
|
||||||
if !s.matchesTags(tags) {
|
if !s.matchesTags(tags) {
|
||||||
t.Errorf(`expected tags: %v to match tag filter "%s", but it did not`, tags, filter)
|
t.Errorf(`expected tags: %v to match tag filter "%s", but it did not`, tags, filter)
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче