Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
Adds the basic wrapping logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ameteiko committed Aug 1, 2022
0 parents commit 798e9e9
Show file tree
Hide file tree
Showing 7 changed files with 490 additions and 0 deletions.
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/kyma-incubator/testdrape

go 1.18

require github.com/cucumber/godog v0.12.5

require (
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
github.com/hashicorp/go-memdb v1.3.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)
314 changes: 314 additions & 0 deletions go.sum

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions godog/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Godog step adapter

This step adapter allows to switch from the [godog](https://github.com/cucumber/godog) step definition format:

```go
func shouldBeEqualDefault(arg1 int, arg2 int) error {
if arg1 != arg2 {
return errors.New("strings are not equal")
}

return nil
}

func TestFeatures(t *gotesting.T) {
suite := godog.TestSuite{
ScenarioInitializer: func(s *godog.ScenarioContext) {
s.Step(`^(\d+) equals to (\d+)$`, shouldBeEqualDefault)
}
}

if suite.Run() != 0 {
t.Fatal("non-zero status returned, failed to run feature tests")
}
}
```

to the go testing format:
```go
import ("github.com/kyma-incubator/testdrape/godog/testing")

func shouldBeEqual(t *testing.T, arg1 int, arg2 int) {
assert.Equal(t, arg1, arg2)
}

func TestFeaturesWrapped(t *gotesting.T) {
tt := tester{}

suite := godog.TestSuite{
ScenarioInitializer: func(s *godog.ScenarioContext) {
testing.NewContext(s).Register(`^(\d+) equals to (\d+)$`, shouldBeEqual)
}
}

if suite.Run() != 0 {
t.Fatal("non-zero status returned, failed to run feature tests")
}
}
```
21 changes: 21 additions & 0 deletions godog/testing/step.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package testing

import (
"github.com/cucumber/godog"
)

type ctx struct {
ct *godog.ScenarioContext
}

func (c *ctx) Register(expr, stepFunc interface{}) *ctx {
c.ct.Step(expr, AdaptTestFunction(stepFunc))

return c
}

func NewContext(c *godog.ScenarioContext) *ctx {
return &ctx{
ct: c,
}
}
26 changes: 26 additions & 0 deletions godog/testing/t.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package testing

import (
"fmt"
)

// testingT is an interface wrapper around *testing.T
type testingT interface {
Errorf(format string, args ...interface{})
}

// T is the minimal testify-conforming testing object.
type T struct {
err error
}

func (t *T) Errorf(format string, args ...interface{}) {
t.err = fmt.Errorf(format, args...)
}
func (t *T) Err() error {
return t.err
}

func newT() *T {
return &T{}
}
63 changes: 63 additions & 0 deletions godog/testing/wrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package testing

import (
"fmt"
"reflect"
)

// AdaptTestFunction adapts a test function to conform to the Godog runner.
//
// The Godog step runner relies on returning an error for the failed steps.
// As it contradicts the go testing paradigm, this decorator function converts
// a conventional go test function to the one the Godog runner expects.
//
// Performs a conversion:
// func (t *testing.T, arg... any) -> func(arg... any) error
func AdaptTestFunction(testFunc interface{}) interface{} {
validateTestFunction(reflect.ValueOf(testFunc).Type())
nilError := reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())

ins := fetchTestFunctionArguments(reflect.TypeOf(testFunc))
outType := nilError.Type()
adapterType := reflect.FuncOf(ins, []reflect.Type{outType}, false)

return reflect.MakeFunc(adapterType, func(in []reflect.Value) []reflect.Value {
// Inject the *testing.T argument to the argument list.
t := newT()
ins := append([]reflect.Value{reflect.ValueOf(t)}, in...)

// Invoke the test function.
if reflect.ValueOf(testFunc).Call(ins); t.Err() != nil {
return []reflect.Value{reflect.ValueOf(t.Err())}
}

return []reflect.Value{nilError}
}).Interface()
}

func validateTestFunction(t reflect.Type) {
if t.Kind() != reflect.Func {
panic(fmt.Sprintf("handler is not a func, but: %T", t))
}

if outs := t.NumOut(); outs > 0 {
panic(fmt.Sprintf("handler must not return any values, but returns %d", outs))
}

if ins := t.NumIn(); ins == 0 {
panic(fmt.Sprintf("handler must have at least *testing.T argument"))
}

if test := t.In(0); test.Kind() != reflect.Pointer || test.Elem().Implements(reflect.TypeOf((*testingT)(nil)).Elem()) {
panic(fmt.Sprintf("the first argument must be of type *testing.T"))
}
}

func fetchTestFunctionArguments(t reflect.Type) []reflect.Type {
ins := make([]reflect.Type, t.NumIn()-1)
for i := 1; i < t.NumIn(); i++ {
ins[i-1] = t.In(i)
}

return ins
}
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# The Kyma testing wrapper

The repository provides the testing modules and adapters.

0 comments on commit 798e9e9

Please sign in to comment.