update documentation
Этот коммит содержится в:
родитель
e8f6030616
коммит
bef768a5c3
4 изменённых файлов: 162 добавлений и 28 удалений
60
README.md
60
README.md
|
@ -46,24 +46,25 @@ You should see that the steps are undefined:
|
|||
|
||||

|
||||
|
||||
**NOTE:** the undefined step templates are still in development and scheduled for **0.2.0**
|
||||
It gives you undefined step snippets to implement in your test context. You may copy these snippets
|
||||
into **godog_test.go** file.
|
||||
|
||||
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.
|
||||
|
||||
Since we need a working implementation, we may start by implementing what is necessary.
|
||||
We only need a number of **godogs** for now.
|
||||
|
||||
``` go
|
||||
/* file: /tmp/godog/godog.go */
|
||||
package main
|
||||
|
||||
type GodogCart struct {
|
||||
reserve int
|
||||
}
|
||||
|
||||
func (c *GodogCart) Eat(num int) { c.reserve -= num }
|
||||
|
||||
func (c *GodogCart) Available() int { return c.reserve }
|
||||
var Godogs int
|
||||
|
||||
func main() { /* usual main func */ }
|
||||
```
|
||||
|
||||
Now lets describe all steps to test the application behavior:
|
||||
Now lets finish our step implementations in order to test our feature requirements:
|
||||
|
||||
``` go
|
||||
/* file: /tmp/godog/godog_test.go */
|
||||
|
@ -75,35 +76,34 @@ import (
|
|||
"github.com/DATA-DOG/godog"
|
||||
)
|
||||
|
||||
func (c *GodogCart) resetReserve(interface{}) {
|
||||
c.reserve = 0
|
||||
}
|
||||
|
||||
func (c *GodogCart) thereAreNumGodogsInReserve(avail int) error {
|
||||
c.reserve = avail
|
||||
func thereAreGodogs(available int) error {
|
||||
Godogs = available
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GodogCart) iEatNum(num int) error {
|
||||
c.Eat(num)
|
||||
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 (c *GodogCart) thereShouldBeNumRemaining(left int) error {
|
||||
if c.Available() != left {
|
||||
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", left, c.Available())
|
||||
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 godogCartContext(s godog.Suite) {
|
||||
c := &GodogCart{}
|
||||
// each time before running scenario reset reserve
|
||||
s.BeforeScenario(c.resetReserve)
|
||||
// register steps
|
||||
s.Step(`^there are (\d+) godogs?$`, c.thereAreNumGodogsInReserve)
|
||||
s.Step(`^I eat (\d+)$`, c.iEatNum)
|
||||
s.Step(`^there should be (\d+) remaining$`, c.thereShouldBeNumRemaining)
|
||||
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
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -111,6 +111,10 @@ Now when you run the `godog godog.feature` again, you should see:
|
|||
|
||||

|
||||
|
||||
**Note:** we have hooked to **BeforeScenario** event in order to reset state. You may hook into
|
||||
more events, like **AfterStep** to test against an error and print more details about the error
|
||||
or state before failure. Or **BeforeSuite** to prepare a database.
|
||||
|
||||
### Documentation
|
||||
|
||||
See [godoc][godoc] for general API details.
|
||||
|
|
130
fmt.go
130
fmt.go
|
@ -1,13 +1,45 @@
|
|||
package godog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/cucumber/gherkin-go"
|
||||
)
|
||||
|
||||
// some snippet formatting regexps
|
||||
var snippetExprCleanup = regexp.MustCompile("([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+\\'])")
|
||||
var snippetExprQuoted = regexp.MustCompile("(\\s*|^)\"(?:[^\"]*)\"(\\s+|$)")
|
||||
var snippetMethodName = regexp.MustCompile("[^a-zA-Z\\_\\ ]")
|
||||
var snippetNumbers = regexp.MustCompile("(\\d+)")
|
||||
|
||||
var snippetHelperFuncs = template.FuncMap{
|
||||
"backticked": func(s string) string {
|
||||
return "`" + s + "`"
|
||||
},
|
||||
}
|
||||
|
||||
var undefinedSnippetsTpl = template.Must(template.New("snippets").Funcs(snippetHelperFuncs).Parse(`
|
||||
{{ range . }}func {{ .Method }}({{ .Args }}) error {
|
||||
return godog.ErrPending
|
||||
}
|
||||
|
||||
{{end}}func featureContext(s godog.Suite) { {{ range . }}
|
||||
s.Step({{ backticked .Expr }}, {{ .Method }}){{end}}
|
||||
}
|
||||
`))
|
||||
|
||||
type undefinedSnippet struct {
|
||||
Method string
|
||||
Expr string
|
||||
argument interface{} // gherkin step argument
|
||||
}
|
||||
|
||||
type registeredFormatter struct {
|
||||
name string
|
||||
fmt Formatter
|
||||
|
@ -180,6 +212,8 @@ func (f *basefmt) Summary() {
|
|||
steps = append(steps, parts[len(parts)-1])
|
||||
}
|
||||
if len(f.pending) > 0 {
|
||||
passed -= len(f.pending)
|
||||
parts = append(parts, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
|
||||
steps = append(steps, cl(fmt.Sprintf("%d pending", len(f.pending)), yellow))
|
||||
}
|
||||
if len(f.undefined) > 0 {
|
||||
|
@ -209,4 +243,100 @@ func (f *basefmt) Summary() {
|
|||
fmt.Println(fmt.Sprintf("%d steps (%s)", nsteps, strings.Join(steps, ", ")))
|
||||
}
|
||||
fmt.Println(elapsed)
|
||||
|
||||
f.snippets()
|
||||
}
|
||||
|
||||
func (s *undefinedSnippet) Args() string {
|
||||
var args []string
|
||||
var pos, idx int
|
||||
var breakLoop bool
|
||||
for !breakLoop {
|
||||
part := s.Expr[pos:]
|
||||
ipos := strings.Index(part, "(\\d+)")
|
||||
spos := strings.Index(part, "\"([^\"]*)\"")
|
||||
switch {
|
||||
case spos == -1 && ipos == -1:
|
||||
breakLoop = true
|
||||
case spos == -1:
|
||||
idx++
|
||||
pos += ipos + len("(\\d+)")
|
||||
args = append(args, fmt.Sprintf("arg%d int", idx))
|
||||
case ipos == -1:
|
||||
idx++
|
||||
pos += spos + len("\"([^\"]*)\"")
|
||||
args = append(args, fmt.Sprintf("arg%d string", idx))
|
||||
case ipos < spos:
|
||||
idx++
|
||||
pos += ipos + len("(\\d+)")
|
||||
args = append(args, fmt.Sprintf("arg%d int", idx))
|
||||
case spos < ipos:
|
||||
idx++
|
||||
pos += spos + len("\"([^\"]*)\"")
|
||||
args = append(args, fmt.Sprintf("arg%d string", idx))
|
||||
}
|
||||
}
|
||||
if s.argument != nil {
|
||||
idx++
|
||||
switch s.argument.(type) {
|
||||
case *gherkin.DocString:
|
||||
args = append(args, fmt.Sprintf("arg%d *gherkin.DocString", idx))
|
||||
case *gherkin.DataTable:
|
||||
args = append(args, fmt.Sprintf("arg%d *gherkin.DataTable", idx))
|
||||
}
|
||||
}
|
||||
return strings.Join(args, ", ")
|
||||
}
|
||||
|
||||
func (f *basefmt) snippets() {
|
||||
if len(f.undefined) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(cl("\nYou can implement step definitions for undefined steps with these snippets:", yellow))
|
||||
|
||||
var index int
|
||||
var snips []*undefinedSnippet
|
||||
// build snippets
|
||||
for _, u := range f.undefined {
|
||||
expr := snippetExprCleanup.ReplaceAllString(u.step.Text, "\\\\$1")
|
||||
expr = snippetNumbers.ReplaceAllString(expr, "(\\d+)")
|
||||
expr = snippetExprQuoted.ReplaceAllString(expr, " \"([^\"]*)\" ")
|
||||
expr = "^" + strings.TrimSpace(expr) + "$"
|
||||
|
||||
name := snippetNumbers.ReplaceAllString(u.step.Text, "")
|
||||
name = snippetExprQuoted.ReplaceAllString(name, "")
|
||||
name = snippetMethodName.ReplaceAllString(name, "")
|
||||
var words []string
|
||||
for i, w := range strings.Split(name, " ") {
|
||||
if i != 0 {
|
||||
w = strings.Title(w)
|
||||
} else {
|
||||
w = string(unicode.ToLower(rune(w[0]))) + w[1:]
|
||||
}
|
||||
words = append(words, w)
|
||||
}
|
||||
name = strings.Join(words, "")
|
||||
if len(name) == 0 {
|
||||
index++
|
||||
name = fmt.Sprintf("stepDefinition%d", index)
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, snip := range snips {
|
||||
if snip.Expr == expr {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
snips = append(snips, &undefinedSnippet{Method: name, Expr: expr, argument: u.step.Argument})
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := undefinedSnippetsTpl.Execute(&buf, snips); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(cl(buf.String(), yellow))
|
||||
}
|
||||
|
|
Двоичные данные
screenshots/passed.png
Двоичные данные
screenshots/passed.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 61 КиБ После Ширина: | Высота: | Размер: 54 КиБ |
Двоичные данные
screenshots/undefined.png
Двоичные данные
screenshots/undefined.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 47 КиБ После Ширина: | Высота: | Размер: 96 КиБ |
Загрузка…
Создание таблицы
Сослаться в новой задаче