Merge pull request #364 from cucumber/allow_empty_return_step
feat(step_definition): Allows to define step function without return.
Этот коммит содержится в:
коммит
15358d20e7
11 изменённых файлов: 326 добавлений и 45 удалений
|
@ -12,6 +12,8 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Support for step definitions without return ([364](https://github.com/cucumber/godog/pull/364) -[titouanfreville])
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Upgraded gherkin-go to v19 ([402](https://github.com/cucumber/godog/pull/402) - [mbow])
|
* Upgraded gherkin-go to v19 ([402](https://github.com/cucumber/godog/pull/402) - [mbow])
|
||||||
|
|
|
@ -210,6 +210,14 @@ Feature: eat godogs
|
||||||
|
|
||||||
You may change **return godog.ErrPending** to **return nil** in the three step definitions and the scenario will pass successfully.
|
You may change **return godog.ErrPending** to **return nil** in the three step definitions and the scenario will pass successfully.
|
||||||
|
|
||||||
|
Also, you may omit error return if your step does not fail.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func iEat(arg1 int) {
|
||||||
|
// Eat arg1.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### Step 5 - Create the main program to test
|
#### Step 5 - Create the main program to test
|
||||||
|
|
||||||
We only need a number of **godogs** for now. Lets keep it simple.
|
We only need a number of **godogs** for now. Lets keep it simple.
|
||||||
|
|
|
@ -10,6 +10,7 @@ Feature: tag filters
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given passing step
|
Given passing step
|
||||||
|
And passing step without return
|
||||||
|
|
||||||
Scenario Outline: parse a scenario
|
Scenario Outline: parse a scenario
|
||||||
Given a feature path "<path>"
|
Given a feature path "<path>"
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -7,6 +7,7 @@ require (
|
||||||
github.com/cucumber/messages-go/v16 v16.0.1
|
github.com/cucumber/messages-go/v16 v16.0.1
|
||||||
github.com/hashicorp/go-memdb v1.3.0
|
github.com/hashicorp/go-memdb v1.3.0
|
||||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||||
|
github.com/smartystreets/goconvey v1.6.4
|
||||||
github.com/spf13/cobra v1.1.1
|
github.com/spf13/cobra v1.1.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -71,6 +71,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
@ -109,6 +110,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
@ -161,7 +163,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -12,6 +13,13 @@ import (
|
||||||
|
|
||||||
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
var typeOfBytes = reflect.TypeOf([]byte(nil))
|
||||||
|
|
||||||
|
// matchable errors
|
||||||
|
var (
|
||||||
|
ErrUnmatchedStepArgumentNumber = errors.New("func received more arguments than expected")
|
||||||
|
ErrCannotConvert = errors.New("cannot convert argument")
|
||||||
|
ErrUnsupportedArgumentType = errors.New("unsupported argument type")
|
||||||
|
)
|
||||||
|
|
||||||
// StepDefinition ...
|
// StepDefinition ...
|
||||||
type StepDefinition struct {
|
type StepDefinition struct {
|
||||||
formatters.StepDefinition
|
formatters.StepDefinition
|
||||||
|
@ -28,8 +36,9 @@ type StepDefinition struct {
|
||||||
func (sd *StepDefinition) Run() interface{} {
|
func (sd *StepDefinition) Run() interface{} {
|
||||||
typ := sd.HandlerValue.Type()
|
typ := sd.HandlerValue.Type()
|
||||||
if len(sd.Args) < typ.NumIn() {
|
if len(sd.Args) < typ.NumIn() {
|
||||||
return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.Args))
|
return fmt.Errorf("%w: expected %d arguments, matched %d from step", ErrUnmatchedStepArgumentNumber, typ.NumIn(), len(sd.Args))
|
||||||
}
|
}
|
||||||
|
|
||||||
var values []reflect.Value
|
var values []reflect.Value
|
||||||
for i := 0; i < typ.NumIn(); i++ {
|
for i := 0; i < typ.NumIn(); i++ {
|
||||||
param := typ.In(i)
|
param := typ.In(i)
|
||||||
|
@ -41,7 +50,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseInt(s, 10, 0)
|
v, err := strconv.ParseInt(s, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to int: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(int(v)))
|
values = append(values, reflect.ValueOf(int(v)))
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
|
@ -51,9 +60,9 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseInt(s, 10, 64)
|
v, err := strconv.ParseInt(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int64: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to int64: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(int64(v)))
|
values = append(values, reflect.ValueOf(v))
|
||||||
case reflect.Int32:
|
case reflect.Int32:
|
||||||
s, err := sd.shouldBeString(i)
|
s, err := sd.shouldBeString(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,7 +70,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseInt(s, 10, 32)
|
v, err := strconv.ParseInt(s, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int32: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to int32: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(int32(v)))
|
values = append(values, reflect.ValueOf(int32(v)))
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
|
@ -71,7 +80,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseInt(s, 10, 16)
|
v, err := strconv.ParseInt(s, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int16: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to int16: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(int16(v)))
|
values = append(values, reflect.ValueOf(int16(v)))
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
|
@ -81,7 +90,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseInt(s, 10, 8)
|
v, err := strconv.ParseInt(s, 10, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to int8: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to int8: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(int8(v)))
|
values = append(values, reflect.ValueOf(int8(v)))
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
@ -97,7 +106,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseFloat(s, 64)
|
v, err := strconv.ParseFloat(s, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float64: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to float64: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(v))
|
values = append(values, reflect.ValueOf(v))
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
|
@ -107,7 +116,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
v, err := strconv.ParseFloat(s, 32)
|
v, err := strconv.ParseFloat(s, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%s" to float32: %s`, i, s, err)
|
return fmt.Errorf(`%w %d: "%s" to float32: %s`, ErrCannotConvert, i, s, err)
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf(float32(v)))
|
values = append(values, reflect.ValueOf(float32(v)))
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
|
@ -124,7 +133,7 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleDocString`, i, arg, arg)
|
return fmt.Errorf(`%w %d: "%v" of type "%T" to *messages.PickleDocString`, ErrCannotConvert, i, arg, arg)
|
||||||
case "messages.PickleTable":
|
case "messages.PickleTable":
|
||||||
if v, ok := arg.(*messages.PickleStepArgument); ok {
|
if v, ok := arg.(*messages.PickleStepArgument); ok {
|
||||||
values = append(values, reflect.ValueOf(v.DataTable))
|
values = append(values, reflect.ValueOf(v.DataTable))
|
||||||
|
@ -136,9 +145,9 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleTable`, i, arg, arg)
|
return fmt.Errorf(`%w %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleTable`, ErrCannotConvert, i, arg, arg)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("the argument %d type %T is not supported %s", i, arg, param.Elem().String())
|
return fmt.Errorf("%w: the argument %d type %T is not supported %s", ErrUnsupportedArgumentType, i, arg, param.Elem().String())
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
switch param {
|
switch param {
|
||||||
|
@ -149,14 +158,19 @@ func (sd *StepDefinition) Run() interface{} {
|
||||||
}
|
}
|
||||||
values = append(values, reflect.ValueOf([]byte(s)))
|
values = append(values, reflect.ValueOf([]byte(s)))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("the slice argument %d type %s is not supported", i, param.Kind())
|
return fmt.Errorf("%w: the slice argument %d type %s is not supported", ErrUnsupportedArgumentType, i, param.Kind())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind())
|
return fmt.Errorf("%w: the argument %d type %s is not supported", ErrUnsupportedArgumentType, i, param.Kind())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd.HandlerValue.Call(values)[0].Interface()
|
res := sd.HandlerValue.Call(values)
|
||||||
|
if len(res) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return res[0].Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
|
func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
|
||||||
|
@ -166,13 +180,13 @@ func (sd *StepDefinition) shouldBeString(idx int) (string, error) {
|
||||||
return arg, nil
|
return arg, nil
|
||||||
case *messages.PickleStepArgument:
|
case *messages.PickleStepArgument:
|
||||||
if arg.DocString == nil {
|
if arg.DocString == nil {
|
||||||
return "", fmt.Errorf(`cannot convert DocString is not set`)
|
return "", fmt.Errorf(`%w %d: "%v" of type "%T": DocString is not set`, ErrCannotConvert, idx, arg, arg)
|
||||||
}
|
}
|
||||||
return arg.DocString.Content, nil
|
return arg.DocString.Content, nil
|
||||||
case *messages.PickleDocString:
|
case *messages.PickleDocString:
|
||||||
return arg.Content, nil
|
return arg.Content, nil
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to string`, idx, arg, arg)
|
return "", fmt.Errorf(`%w %d: "%v" of type "%T" to string`, ErrCannotConvert, idx, arg, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package models_test
|
package models_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -8,10 +9,32 @@ import (
|
||||||
|
|
||||||
"github.com/cucumber/messages-go/v16"
|
"github.com/cucumber/messages-go/v16"
|
||||||
|
|
||||||
|
"github.com/cucumber/godog"
|
||||||
"github.com/cucumber/godog/formatters"
|
"github.com/cucumber/godog/formatters"
|
||||||
"github.com/cucumber/godog/internal/models"
|
"github.com/cucumber/godog/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestShouldSupportEmptyHandlerReturn(t *testing.T) {
|
||||||
|
fn := func(a int64, b int32, c int16, d int8) {}
|
||||||
|
|
||||||
|
def := &models.StepDefinition{
|
||||||
|
StepDefinition: formatters.StepDefinition{
|
||||||
|
Handler: fn,
|
||||||
|
},
|
||||||
|
HandlerValue: reflect.ValueOf(fn),
|
||||||
|
}
|
||||||
|
|
||||||
|
def.Args = []interface{}{"1", "1", "1", "1"}
|
||||||
|
if err := def.Run(); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
def.Args = []interface{}{"1", "1", "1", strings.Repeat("1", 9)}
|
||||||
|
if err := def.Run(); err == nil {
|
||||||
|
t.Fatalf("expected convertion fail for int8, but got none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldSupportIntTypes(t *testing.T) {
|
func TestShouldSupportIntTypes(t *testing.T) {
|
||||||
fn := func(a int64, b int32, c int16, d int8) error { return nil }
|
fn := func(a int64, b int32, c int16, d int8) error { return nil }
|
||||||
|
|
||||||
|
@ -132,13 +155,165 @@ func TestUnexpectedArguments(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
def.Args = []interface{}{"1"}
|
def.Args = []interface{}{"1"}
|
||||||
if err := def.Run(); err == nil {
|
|
||||||
|
res := def.Run()
|
||||||
|
if res == nil {
|
||||||
t.Fatalf("expected an error due to wrong number of arguments, but got none")
|
t.Fatalf("expected an error due to wrong number of arguments, but got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
def.Args = []interface{}{"one", "two"}
|
err, ok := res.(error)
|
||||||
if err := def.Run(); err == nil {
|
if !ok {
|
||||||
t.Fatalf("expected conversion error, but got none")
|
t.Fatalf("expected an error due to wrong number of arguments, but got %T instead", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, models.ErrUnmatchedStepArgumentNumber) {
|
||||||
|
t.Fatalf("expected an error due to wrong number of arguments, but got %v instead", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepDefinition_Run_StepShouldBeString(t *testing.T) {
|
||||||
|
test := func(t *testing.T, fn interface{}) {
|
||||||
|
def := &models.StepDefinition{
|
||||||
|
StepDefinition: formatters.StepDefinition{
|
||||||
|
Handler: fn,
|
||||||
|
},
|
||||||
|
HandlerValue: reflect.ValueOf(fn),
|
||||||
|
}
|
||||||
|
|
||||||
|
def.Args = []interface{}{12}
|
||||||
|
|
||||||
|
res := def.Run()
|
||||||
|
if res == nil {
|
||||||
|
t.Fatalf("expected a string convertion error, but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
err, ok := res.(error)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected a string convertion error, but got %T instead", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, models.ErrCannotConvert) {
|
||||||
|
t.Fatalf("expected a string convertion error, but got '%v' instead", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure step type error if step argument is not a string
|
||||||
|
// for all supported types.
|
||||||
|
test(t, func(a int) error { return nil })
|
||||||
|
test(t, func(a int64) error { return nil })
|
||||||
|
test(t, func(a int32) error { return nil })
|
||||||
|
test(t, func(a int16) error { return nil })
|
||||||
|
test(t, func(a int8) error { return nil })
|
||||||
|
test(t, func(a string) error { return nil })
|
||||||
|
test(t, func(a float64) error { return nil })
|
||||||
|
test(t, func(a float32) error { return nil })
|
||||||
|
test(t, func(a *godog.Table) error { return nil })
|
||||||
|
test(t, func(a *godog.DocString) error { return nil })
|
||||||
|
test(t, func(a []byte) error { return nil })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepDefinition_Run_InvalidHandlerParamConversion(t *testing.T) {
|
||||||
|
test := func(t *testing.T, fn interface{}) {
|
||||||
|
def := &models.StepDefinition{
|
||||||
|
StepDefinition: formatters.StepDefinition{
|
||||||
|
Handler: fn,
|
||||||
|
},
|
||||||
|
HandlerValue: reflect.ValueOf(fn),
|
||||||
|
}
|
||||||
|
|
||||||
|
def.Args = []interface{}{12}
|
||||||
|
|
||||||
|
res := def.Run()
|
||||||
|
if res == nil {
|
||||||
|
t.Fatalf("expected an unsupported argument type error, but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
err, ok := res.(error)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected an unsupported argument type error, but got %T instead", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, models.ErrUnsupportedArgumentType) {
|
||||||
|
t.Fatalf("expected an unsupported argument type error, but got '%v' instead", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists some unsupported argument types for step handler.
|
||||||
|
|
||||||
|
// Pointers should work only for godog.Table/godog.DocString
|
||||||
|
test(t, func(a *int) error { return nil })
|
||||||
|
test(t, func(a *int64) error { return nil })
|
||||||
|
test(t, func(a *int32) error { return nil })
|
||||||
|
test(t, func(a *int16) error { return nil })
|
||||||
|
test(t, func(a *int8) error { return nil })
|
||||||
|
test(t, func(a *string) error { return nil })
|
||||||
|
test(t, func(a *float64) error { return nil })
|
||||||
|
test(t, func(a *float32) error { return nil })
|
||||||
|
|
||||||
|
// I cannot pass structures
|
||||||
|
test(t, func(a godog.Table) error { return nil })
|
||||||
|
test(t, func(a godog.DocString) error { return nil })
|
||||||
|
test(t, func(a testStruct) error { return nil })
|
||||||
|
|
||||||
|
// I cannot use maps
|
||||||
|
test(t, func(a map[string]interface{}) error { return nil })
|
||||||
|
test(t, func(a map[string]int) error { return nil })
|
||||||
|
|
||||||
|
// Slice works only for byte
|
||||||
|
test(t, func(a []int) error { return nil })
|
||||||
|
test(t, func(a []string) error { return nil })
|
||||||
|
test(t, func(a []bool) error { return nil })
|
||||||
|
|
||||||
|
// I cannot use bool
|
||||||
|
test(t, func(a bool) error { return nil })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepDefinition_Run_StringConversionToFunctionType(t *testing.T) {
|
||||||
|
test := func(t *testing.T, fn interface{}, args []interface{}) {
|
||||||
|
def := &models.StepDefinition{
|
||||||
|
StepDefinition: formatters.StepDefinition{
|
||||||
|
Handler: fn,
|
||||||
|
},
|
||||||
|
HandlerValue: reflect.ValueOf(fn),
|
||||||
|
Args: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := def.Run()
|
||||||
|
if res == nil {
|
||||||
|
t.Fatalf("expected a cannot convert argument type error, but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
err, ok := res.(error)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected a cannot convert argument type error, but got %T instead", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, models.ErrCannotConvert) {
|
||||||
|
t.Fatalf("expected a cannot convert argument type error, but got '%v' instead", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists some unsupported argument types for step handler.
|
||||||
|
|
||||||
|
// Cannot convert invalid int
|
||||||
|
test(t, func(a int) error { return nil }, []interface{}{"a"})
|
||||||
|
test(t, func(a int64) error { return nil }, []interface{}{"a"})
|
||||||
|
test(t, func(a int32) error { return nil }, []interface{}{"a"})
|
||||||
|
test(t, func(a int16) error { return nil }, []interface{}{"a"})
|
||||||
|
test(t, func(a int8) error { return nil }, []interface{}{"a"})
|
||||||
|
|
||||||
|
// Cannot convert invalid float
|
||||||
|
test(t, func(a float32) error { return nil }, []interface{}{"a"})
|
||||||
|
test(t, func(a float64) error { return nil }, []interface{}{"a"})
|
||||||
|
|
||||||
|
// Cannot convert to DataArg
|
||||||
|
test(t, func(a *godog.Table) error { return nil }, []interface{}{"194"})
|
||||||
|
|
||||||
|
// Cannot convert to DocString ?
|
||||||
|
test(t, func(a *godog.DocString) error { return nil }, []interface{}{"194"})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO maybe we should support duration
|
// @TODO maybe we should support duration
|
||||||
|
@ -149,6 +324,9 @@ func TestUnexpectedArguments(t *testing.T) {
|
||||||
// if err := def.Run(); err == nil {
|
// if err := def.Run(); err == nil {
|
||||||
// t.Fatalf("expected an error due to wrong argument type, but got none")
|
// t.Fatalf("expected an error due to wrong argument type, but got none")
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
type testStruct struct {
|
||||||
|
a string
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldSupportDocStringToStringConversion(t *testing.T) {
|
func TestShouldSupportDocStringToStringConversion(t *testing.T) {
|
||||||
|
|
|
@ -569,6 +569,7 @@ type progressOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func passingStepDef() error { return nil }
|
func passingStepDef() error { return nil }
|
||||||
|
func passingStepDefWithoutReturn() {}
|
||||||
|
|
||||||
func oddEvenStepDef(odd, even int) error { return oddOrEven(odd, even) }
|
func oddEvenStepDef(odd, even int) error { return oddOrEven(odd, even) }
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ func InitializeScenario(ctx *ScenarioContext) {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.Step(`^(?:a )?passing step without return$`, func() {})
|
||||||
ctx.BeforeStep(tc.inject)
|
ctx.BeforeStep(tc.inject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,9 +425,8 @@ func (tc *godogFeaturesScenario) aFeatureFile(path string, body *DocString) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *godogFeaturesScenario) featurePath(path string) error {
|
func (tc *godogFeaturesScenario) featurePath(path string) {
|
||||||
tc.paths = append(tc.paths, path)
|
tc.paths = append(tc.paths, path)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *godogFeaturesScenario) parseFeatures() error {
|
func (tc *godogFeaturesScenario) parseFeatures() error {
|
||||||
|
|
|
@ -12,6 +12,9 @@ import (
|
||||||
"github.com/cucumber/godog/internal/models"
|
"github.com/cucumber/godog/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// matchable errors
|
||||||
|
var ()
|
||||||
|
|
||||||
// Scenario represents the executed scenario
|
// Scenario represents the executed scenario
|
||||||
type Scenario = messages.Pickle
|
type Scenario = messages.Pickle
|
||||||
|
|
||||||
|
@ -176,8 +179,8 @@ func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
|
||||||
panic(fmt.Sprintf("expected handler to be func, but got: %T", stepFunc))
|
panic(fmt.Sprintf("expected handler to be func, but got: %T", stepFunc))
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ.NumOut() != 1 {
|
if typ.NumOut() > 1 {
|
||||||
panic(fmt.Sprintf("expected handler to return only one value, but it has: %d", typ.NumOut()))
|
panic(fmt.Sprintf("expected handler to return either zero or one value, but it has: %d", typ.NumOut()))
|
||||||
}
|
}
|
||||||
|
|
||||||
def := &models.StepDefinition{
|
def := &models.StepDefinition{
|
||||||
|
@ -188,6 +191,7 @@ func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
|
||||||
HandlerValue: v,
|
HandlerValue: v,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if typ.NumOut() == 1 {
|
||||||
typ = typ.Out(0)
|
typ = typ.Out(0)
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
|
@ -196,12 +200,13 @@ func (ctx *ScenarioContext) Step(expr, stepFunc interface{}) {
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if typ.Elem().Kind() != reflect.String {
|
if typ.Elem().Kind() != reflect.String {
|
||||||
panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Kind()))
|
panic(fmt.Sprintf("expected handler to return []string for multistep, but got: []%s", typ.Elem().Kind()))
|
||||||
}
|
}
|
||||||
def.Nested = true
|
def.Nested = true
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
|
panic(fmt.Sprintf("expected handler to return an error or []string, but got: %s", typ.Kind()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.suite.steps = append(ctx.suite.steps, def)
|
ctx.suite.steps = append(ctx.suite.steps, def)
|
||||||
}
|
}
|
||||||
|
|
67
test_context_test.go
Обычный файл
67
test_context_test.go
Обычный файл
|
@ -0,0 +1,67 @@
|
||||||
|
package godog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScenarioContext_Step(t *testing.T) {
|
||||||
|
Convey("When adding steps to ScenarioContext ", t, func() {
|
||||||
|
ctx := ScenarioContext{suite: &suite{}}
|
||||||
|
|
||||||
|
Convey("It should accept steps defined with regexp.Regexp", func() {
|
||||||
|
re := regexp.MustCompile(`(?:it is a test)?.{10}x*`)
|
||||||
|
So(func() { ctx.Step(re, okEmptyResult) }, ShouldNotPanic)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("It should accept steps defined with bytes slice", func() {
|
||||||
|
So(func() { ctx.Step([]byte("(?:it is a test)?.{10}x*"), okEmptyResult) }, ShouldNotPanic)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("It should accept steps handler with empty return", func() {
|
||||||
|
So(func() { ctx.Step(".*", okEmptyResult) }, ShouldNotPanic)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("It should accept steps handler with error return", func() {
|
||||||
|
So(func() { ctx.Step(".*", okErrorResult) }, ShouldNotPanic)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("It should accept steps handler with string slice return", func() {
|
||||||
|
So(func() { ctx.Step(".*", okSliceResult) }, ShouldNotPanic)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("It should panic if step expression is neither a string, regex or byte slice", func() {
|
||||||
|
So(func() { ctx.Step(1251, okSliceResult) }, ShouldPanicWith, fmt.Sprintf("expecting expr to be a *regexp.Regexp or a string, got type: %T", 12))
|
||||||
|
})
|
||||||
|
Convey("It should panic if step handler", func() {
|
||||||
|
Convey("is not a function", func() {
|
||||||
|
So(func() { ctx.Step(".*", 124) }, ShouldPanicWith, fmt.Sprintf("expected handler to be func, but got: %T", 12))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("has more than 1 return value", func() {
|
||||||
|
So(func() { ctx.Step(".*", nokLimitCase) }, ShouldPanicWith, fmt.Sprintf("expected handler to return either zero or one value, but it has: 2"))
|
||||||
|
So(func() { ctx.Step(".*", nokMore) }, ShouldPanicWith, fmt.Sprintf("expected handler to return either zero or one value, but it has: 5"))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("return type is not an error or string slice or void", func() {
|
||||||
|
So(func() { ctx.Step(".*", nokInvalidReturnInterfaceType) }, ShouldPanicWith, "expected handler to return an error, but got: interface")
|
||||||
|
So(func() { ctx.Step(".*", nokInvalidReturnSliceType) }, ShouldPanicWith, "expected handler to return []string for multistep, but got: []int")
|
||||||
|
So(func() { ctx.Step(".*", nokInvalidReturnOtherType) }, ShouldPanicWith, "expected handler to return an error or []string, but got: chan")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func okEmptyResult() {}
|
||||||
|
func okErrorResult() error { return nil }
|
||||||
|
func okSliceResult() []string { return nil }
|
||||||
|
func nokLimitCase() (int, error) { return 0, nil }
|
||||||
|
func nokMore() (int, int, int, int, error) { return 0, 0, 0, 0, nil }
|
||||||
|
func nokInvalidReturnInterfaceType() interface{} { return 0 }
|
||||||
|
func nokInvalidReturnSliceType() []int { return nil }
|
||||||
|
func nokInvalidReturnOtherType() chan int { return nil }
|
Загрузка…
Создание таблицы
Сослаться в новой задаче