godog/run_test.go
tfreville eb75d692bd <fix>(PRETIFIER): Fix s method to not have errors on negative entry
Context:
While trying to create an helper library to manage http rest api testing, I made a system witch allow to pick value from responses, header, cookie, ... and inject then as variables.
Issue:
Doing this, when the inject variable make the line longer than the longest declared step, godog will failed to render test result under pretty formatting cause it will try to write a comment on a negative index
Fix:
Fix s methods so it will not goes to fatal when recieving negative number.
2020-03-09 10:39:20 +01:00

292 строки
7,1 КиБ
Go

package godog
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cucumber/godog/colors"
"github.com/cucumber/godog/gherkin"
)
func okStep() error {
return nil
}
func TestPrintsStepDefinitions(t *testing.T) {
var buf bytes.Buffer
w := colors.Uncolored(&buf)
s := &Suite{}
steps := []string{
"^passing step$",
`^with name "([^"])"`,
}
for _, step := range steps {
s.Step(step, okStep)
}
s.printStepDefinitions(w)
out := buf.String()
ref := `okStep`
for i, def := range strings.Split(strings.TrimSpace(out), "\n") {
if idx := strings.Index(def, steps[i]); idx == -1 {
t.Fatalf(`step "%s" was not found in output`, steps[i])
}
if idx := strings.Index(def, ref); idx == -1 {
t.Fatalf(`step definition reference "%s" was not found in output: "%s"`, ref, def)
}
}
}
func TestPrintsNoStepDefinitionsIfNoneFound(t *testing.T) {
var buf bytes.Buffer
w := colors.Uncolored(&buf)
s := &Suite{}
s.printStepDefinitions(w)
out := strings.TrimSpace(buf.String())
assert.Equal(t, "there were no contexts registered, could not find any step definition..", out)
}
func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
require.NoError(t, err)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^one$`, func() error { return nil })
s.Step(`^two$`, func() error { return ErrPending })
},
}
assert.False(t, r.run())
r.strict = true
assert.True(t, r.run())
}
func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
require.NoError(t, err)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^one$`, func() error { return nil })
// two - is undefined
},
}
assert.False(t, r.run())
r.strict = true
assert.True(t, r.run())
}
func TestShouldFailOnError(t *testing.T) {
feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature))
require.NoError(t, err)
r := runner{
fmt: progressFunc("progress", ioutil.Discard),
features: []*feature{&feature{Feature: feat}},
initializer: func(s *Suite) {
s.Step(`^one$`, func() error { return nil })
s.Step(`^two$`, func() error { return fmt.Errorf("error") })
},
}
assert.True(t, r.run())
}
func TestFailsWithConcurrencyOptionError(t *testing.T) {
stderr, closer := bufErrorPipe(t)
defer closer()
defer stderr.Close()
opt := Options{
Format: "pretty",
Paths: []string{"features/load:6"},
Concurrency: 2,
Output: ioutil.Discard,
}
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
require.Equal(t, exitOptionError, status)
closer()
b, err := ioutil.ReadAll(stderr)
require.NoError(t, err)
out := strings.TrimSpace(string(b))
assert.Equal(t, `format "pretty" does not support concurrent execution`, out)
}
func TestFailsWithUnknownFormatterOptionError(t *testing.T) {
stderr, closer := bufErrorPipe(t)
defer closer()
defer stderr.Close()
opt := Options{
Format: "unknown",
Paths: []string{"features/load:6"},
Output: ioutil.Discard,
}
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
require.Equal(t, exitOptionError, status)
closer()
b, err := ioutil.ReadAll(stderr)
require.NoError(t, err)
out := strings.TrimSpace(string(b))
assert.Contains(t, out, `unregistered formatter name: "unknown", use one of`)
}
func TestFailsWithOptionErrorWhenLookingForFeaturesInUnavailablePath(t *testing.T) {
stderr, closer := bufErrorPipe(t)
defer closer()
defer stderr.Close()
opt := Options{
Format: "progress",
Paths: []string{"unavailable"},
Output: ioutil.Discard,
}
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
require.Equal(t, exitOptionError, status)
closer()
b, err := ioutil.ReadAll(stderr)
require.NoError(t, err)
out := strings.TrimSpace(string(b))
assert.Equal(t, `feature path "unavailable" is not available`, out)
}
func TestByDefaultRunsFeaturesPath(t *testing.T) {
opt := Options{
Format: "progress",
Output: ioutil.Discard,
Strict: true,
}
status := RunWithOptions("fails", func(_ *Suite) {}, opt)
// should fail in strict mode due to undefined steps
assert.Equal(t, exitFailure, status)
opt.Strict = false
status = RunWithOptions("succeeds", func(_ *Suite) {}, opt)
// should succeed in non strict mode due to undefined steps
assert.Equal(t, exitSuccess, status)
}
func bufErrorPipe(t *testing.T) (io.ReadCloser, func()) {
stderr := os.Stderr
r, w, err := os.Pipe()
require.NoError(t, err)
os.Stderr = w
return r, func() {
w.Close()
os.Stderr = stderr
}
}
func TestFeatureFilePathParser(t *testing.T) {
type Case struct {
input string
path string
line int
}
cases := []Case{
{"/home/test.feature", "/home/test.feature", -1},
{"/home/test.feature:21", "/home/test.feature", 21},
{"test.feature", "test.feature", -1},
{"test.feature:2", "test.feature", 2},
{"", "", -1},
{"/c:/home/test.feature", "/c:/home/test.feature", -1},
{"/c:/home/test.feature:3", "/c:/home/test.feature", 3},
{"D:\\home\\test.feature:3", "D:\\home\\test.feature", 3},
}
for _, c := range cases {
p, ln := extractFeaturePathLine(c.input)
assert.Equal(t, p, c.path)
assert.Equal(t, ln, c.line)
}
}
type succeedRunTestCase struct {
format string // formatter to use
concurrency int // concurrency option range to test
filename string // expected output file
}
func TestSucceedRun(t *testing.T) {
testCases := []succeedRunTestCase{
{format: "progress", concurrency: 4, filename: "fixtures/progress_output.txt"},
{format: "junit", concurrency: 4, filename: "fixtures/junit_output.xml"},
{format: "cucumber", concurrency: 2, filename: "fixtures/cucumber_output.json"},
}
for _, tc := range testCases {
expectedOutputNoConcurrency, err := ioutil.ReadFile(tc.filename)
require.NoError(t, err)
for concurrency := range make([]int, tc.concurrency) {
t.Run(
fmt.Sprintf("%s/concurrency/%d", tc.format, concurrency),
func(t *testing.T) {
testSucceedRun(t, tc.format, concurrency, string(expectedOutputNoConcurrency))
},
)
}
}
}
func testSucceedRun(t *testing.T, format string, concurrency int, expectedOutput string) {
output := new(bytes.Buffer)
opt := Options{
Format: format,
NoColors: true,
Paths: []string{"features"},
Concurrency: concurrency,
Output: output,
}
status := RunWithOptions("succeed", func(s *Suite) { SuiteContext(s) }, opt)
assert.Equal(t, exitSuccess, status)
b, err := ioutil.ReadAll(output)
require.NoError(t, err)
actual := strings.TrimSpace(string(b))
suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`)
expectedOutput = suiteCtxReg.ReplaceAllString(expectedOutput, `suite_context.go:0`)
actual = suiteCtxReg.ReplaceAllString(actual, `suite_context.go:0`)
assert.Equalf(t, expectedOutput, actual, "[%s]", actual)
}