Repository: go-bdd/gobdd
Pull request #100 (2020-05-12 16:33:17)

Add golangci-lint linter

sagikazarmark: Great start, you might want to fine tune the enabled linters though: https://github.com/golangci/golangci-lint#supported-linters

Example: https://github.com/emperror/emperror/blob/master/.golangci.yml

(Note that this example enables all linters by default and disables the one I don't care about)

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e1f7bee..3d15dce 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
 
     steps:
       - name: Set up Go
-        uses: actions/setup-go@v1
+        uses: actions/setup-go@v2
         with:
           go-version: ${{ matrix.go }}
 
diff --git a/.github/workflows/cyclop.yml b/.github/workflows/cyclop.yml
new file mode 100644
index 0000000..f6c30ee
--- /dev/null
+++ b/.github/workflows/cyclop.yml
@@ -0,0 +1,26 @@
+name: Cyclop
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    env:
+      VERBOSE: 1
+      GOFLAGS: -mod=readonly
+      GOPROXY: https://proxy.golang.org
+
+    steps:
+      - name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: '^1.14'
+      - name: Download and cyclop
+        run: |
+          go get github.com/bkielbasa/cyclop
+          cyclop .
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..aff95d1
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,15 @@
+
+name: golangci-lint
+
+on:
+  pull_request:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: golangci-lint
+      uses: actions-contrib/golangci-lint@v1
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..c8683fd
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,12 @@
+linters-settings:
+  golint:
+    min-confidence: 0.1
+
+linters:
+  enable-all: true
+  disable:
+    - funlen
+    - gomnd
+    - golint
+    - scopelint
+    - gosec
diff --git a/context/context.go b/context/context.go
index 586637b..ebad33b 100644
--- a/context/context.go
+++ b/context/context.go
@@ -29,6 +29,7 @@ func (ctx Context) Get(key interface{}, defaultValue ...interface{}) (interface{
 		if len(defaultValue) == 1 {
 			return defaultValue[0], nil
 		}
+
 		return nil, fmt.Errorf("the key %+v does not exist", key)
 	}
 
@@ -41,6 +42,7 @@ func (ctx Context) GetError(key interface{}, defaultValue ...error) (error, erro
 		if len(defaultValue) == 1 {
 			return defaultValue[0], nil
 		}
+
 		return nil, fmt.Errorf("the key %+v does not exist", key)
 	}
 
@@ -52,5 +54,6 @@ func (ctx Context) GetError(key interface{}, defaultValue ...error) (error, erro
 	if !ok {
 		return nil, fmt.Errorf("the expected value is not error  (%T)", key)
 	}
+
 	return value, nil
 }
diff --git a/context/doc.go b/context/doc.go
index 53cbc01..e20de23 100644
--- a/context/doc.go
+++ b/context/doc.go
@@ -6,6 +6,7 @@
 //
 // Good practices
 //
-// It’s a good practice to use custom structs as keys instead of strings or any built-in types to avoid collisions between steps using context.
+// It’s a good practice to use custom structs as keys instead of strings or any built-in types
+// to avoid collisions between steps using context.
 
 package context
diff --git a/doc.go b/doc.go
index 96c867d..bbc5cac 100644
--- a/doc.go
+++ b/doc.go
@@ -1,4 +1,5 @@
-// This project is created as the alternative to https://github.com/DATA-DOG/godog and is inspirited by it. There are a few differences between both solutions:
+// This project is created as the alternative to https://github.com/DATA-DOG/godog and is inspirited by it.
+// There are a few differences between both solutions:
 //
 // - GoBDD uses the built-in testing framework
 //
diff --git a/gen/ctxgen.go b/gen/ctxgen.go
index dde3188..46ce6c4 100644
--- a/gen/ctxgen.go
+++ b/gen/ctxgen.go
@@ -84,7 +84,9 @@ func main() {
 
 	f, err = os.Create("context/get_test.go")
 	die(err)
+
 	tmpl = template.Must(template.New("").Funcs(funcMap).Parse(testTmpl))
+
 	err = tmpl.Execute(f, struct {
 		Types []typeDef
 	}{Types: types})
@@ -98,7 +100,7 @@ func die(err error) {
 	}
 }
 
-var testTmpl = `// Code generated .* DO NOT EDIT.	
+const testTmpl = `// Code generated .* DO NOT EDIT.	
 package context
 
 import "testing"
@@ -161,7 +163,7 @@ func TestContext_Get{{ .Name | Title }}_ErrorOnNotFound(t *testing.T) {
 {{ end }}	
 `
 
-var getTmpl = `// Code generated .* DO NOT EDIT.	
+const getTmpl = `// Code generated .* DO NOT EDIT.	
 package context
 
 import "fmt"
diff --git a/gobdd.go b/gobdd.go
index 79a634f..520812c 100644
--- a/gobdd.go
+++ b/gobdd.go
@@ -15,7 +15,7 @@ import (
 	"testing"
 
 	"github.com/cucumber/gherkin-go/v9"
-	"github.com/cucumber/messages-go/v9"
+	msgs "github.com/cucumber/messages-go/v9"
 	"github.com/go-bdd/gobdd/context"
 )
 
@@ -117,8 +117,9 @@ type TestingT interface {
 // Creates a new suites with given configuration and empty steps defined
 func NewSuite(t TestingT, optionClosures ...func(*SuiteOptions)) *Suite {
 	options := NewSuiteOptions()
-	for _, oC := range optionClosures {
-		oC(&options)
+
+	for i := 0; i < len(optionClosures); i++ {
+		optionClosures[i](&options)
 	}
 
 	return &Suite{
@@ -145,6 +146,7 @@ func (s *Suite) AddStep(expr string, step interface{}) {
 	if err != nil {
 		s.t.Errorf("the step function for step `%s` is incorrect: %w", expr, err)
 		s.hasStepErrors = true
+
 		return
 	}
 
@@ -152,6 +154,7 @@ func (s *Suite) AddStep(expr string, step interface{}) {
 	if err != nil {
 		s.t.Errorf("the step function is incorrect: %w", err)
 		s.hasStepErrors = true
+
 		return
 	}
 
@@ -178,6 +181,7 @@ func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) {
 	if err != nil {
 		s.t.Errorf("the step function is incorrect: %w", err)
 		s.hasStepErrors = true
+
 		return
 	}
 
@@ -191,6 +195,7 @@ func (s *Suite) AddRegexStep(expr *regexp.Regexp, step interface{}) {
 func (s *Suite) Run() {
 	if s.hasStepErrors {
 		s.t.Fatal("the test contains invalid step definitions")
+
 		return
 	}
 
@@ -218,11 +223,13 @@ func (s *Suite) executeFeature(file string) error {
 	}
 	defer f.Close()
 	fileIO := bufio.NewReader(f)
-	doc, err := gherkin.ParseGherkinDocument(fileIO, (&messages.Incrementing{}).NewId)
+
+	doc, err := gherkin.ParseGherkinDocument(fileIO, (&msgs.Incrementing{}).NewId)
 	if err != nil {
 		s.t.Fail()
 		s.t.Errorf("error while loading document: %s\n", err)
-		return fmt.Errorf("error while loading document: %s\n", err)
+
+		return fmt.Errorf("error while loading document: %s", err)
 	}
 
 	if doc.Feature == nil {
@@ -232,17 +239,19 @@ func (s *Suite) executeFeature(file string) error {
 	return s.runFeature(doc.Feature)
 }
 
-func (s *Suite) runFeature(feature *messages.GherkinDocument_Feature) error {
+func (s *Suite) runFeature(feature *msgs.GherkinDocument_Feature) error {
 	log.SetOutput(ioutil.Discard)
+
 	hasErrors := false
 
 	s.t.Run(feature.Name, func(t *testing.T) {
-		var bkgSteps *messages.GherkinDocument_Feature_Background
+		var bkgSteps *msgs.GherkinDocument_Feature_Background
 
 		for _, child := range feature.Children {
 			if child.GetBackground() != nil {
 				bkgSteps = child.GetBackground()
 			}
+
 			scenario := child.GetScenario()
 			if scenario == nil {
 				continue
@@ -253,10 +262,7 @@ func (s *Suite) runFeature(feature *messages.GherkinDocument_Feature) error {
 				continue
 			}
 			ctx := context.New()
-			err := s.runScenario(ctx, scenario, bkgSteps, t)
-			if err != nil {
-				hasErrors = true
-			}
+			s.runScenario(ctx, scenario, bkgSteps, t)
 		}
 	})
 
@@ -267,24 +273,30 @@ func (s *Suite) runFeature(feature *messages.GherkinDocument_Feature) error {
 	return nil
 }
 
-func (s *Suite) getOutlineStep(steps []*messages.GherkinDocument_Feature_Step, examples []*messages.GherkinDocument_Feature_Scenario_Examples) []*messages.GherkinDocument_Feature_Step {
-	var newSteps []*messages.GherkinDocument_Feature_Step
+func (s *Suite) getOutlineStep(steps []*msgs.GherkinDocument_Feature_Step,
+	examples []*msgs.GherkinDocument_Feature_Scenario_Examples) []*msgs.GherkinDocument_Feature_Step {
+	var newSteps []*msgs.GherkinDocument_Feature_Step
+
 	for _, outlineStep := range steps {
 		for _, example := range examples {
 			newSteps = append(newSteps, s.stepsFromExample(outlineStep, example)...)
 		}
 	}
+
 	return newSteps
 }
 
-func (s *Suite) stepsFromExample(sourceStep *messages.GherkinDocument_Feature_Step, example *messages.GherkinDocument_Feature_Scenario_Examples) []*messages.GherkinDocument_Feature_Step {
-	steps := []*messages.GherkinDocument_Feature_Step{}
+func (s *Suite) stepsFromExample(sourceStep *msgs.GherkinDocument_Feature_Step,
+	example *msgs.GherkinDocument_Feature_Scenario_Examples) []*msgs.GherkinDocument_Feature_Step {
+	steps := []*msgs.GherkinDocument_Feature_Step{}
 	text := sourceStep.GetText()
 	placeholders := example.GetTableHeader().GetCells()
+
 	for i, placeholder := range placeholders {
 		ph := "<" + placeholder.GetValue() + ">"
 		index := strings.Index(text, ph)
 		originalText := text
+
 		if index == -1 {
 			continue
 		}
@@ -305,7 +317,7 @@ func (s *Suite) stepsFromExample(sourceStep *messages.GherkinDocument_Feature_St
 	}
 
 	// clone a step
-	step := &messages.GherkinDocument_Feature_Step{
+	step := &msgs.GherkinDocument_Feature_Step{
 		Location: sourceStep.Location,
 		Keyword:  sourceStep.Keyword,
 		Text:     text,
@@ -329,7 +341,8 @@ func (s *Suite) callAfterScenarios(ctx context.Context) {
 	}
 }
 
-func (s *Suite) runScenario(ctx context.Context, scenario *messages.GherkinDocument_Feature_Scenario, bkg *messages.GherkinDocument_Feature_Background, t *testing.T) error {
+func (s *Suite) runScenario(ctx context.Context, scenario *msgs.GherkinDocument_Feature_Scenario,
+	bkg *msgs.GherkinDocument_Feature_Background, t *testing.T) {
 	s.callBeforeScenarios(ctx)
 
 	defer s.callAfterScenarios(ctx)
@@ -344,10 +357,10 @@ func (s *Suite) runScenario(ctx context.Context, scenario *messages.GherkinDocum
 		}
 		s.runSteps(ctx, t, steps)
 	})
-	return nil
 }
 
-func (s *Suite) runSteps(ctx context.Context, t *testing.T, steps []*messages.GherkinDocument_Feature_Step) context.Context {
+func (s *Suite) runSteps(ctx context.Context, t *testing.T,
+	steps []*msgs.GherkinDocument_Feature_Step) context.Context {
 	for _, step := range steps {
 		ctx = s.runStep(ctx, t, step)
 	}
@@ -355,7 +368,7 @@ func (s *Suite) runSteps(ctx context.Context, t *testing.T, steps []*messages.Gh
 	return ctx
 }
 
-func (s *Suite) runStep(ctx context.Context, t *testing.T, step *messages.GherkinDocument_Feature_Step) context.Context {
+func (s *Suite) runStep(ctx context.Context, t *testing.T, step *msgs.GherkinDocument_Feature_Step) context.Context {
 	defer func() {
 		if r := recover(); r != nil {
 			t.Error(r)
@@ -370,13 +383,13 @@ func (s *Suite) runStep(ctx context.Context, t *testing.T, step *messages.Gherki
 
 	params := def.expr.FindSubmatch([]byte(step.Text))[1:]
 	t.Run(step.Text, func(t *testing.T) {
-		ctx = def.run(ctx, t, step, params)
+		ctx = def.run(ctx, t, params)
 	})
 
 	return ctx
 }
 
-func (def *stepDef) run(ctx context.Context, t TestingT, step *messages.GherkinDocument_Feature_Step, params [][]byte) context.Context {
+func (def *stepDef) run(ctx context.Context, t TestingT, params [][]byte) context.Context {
 	defer func() {
 		if r := recover(); r != nil {
 			t.Errorf("%+v", r)
@@ -390,16 +403,19 @@ func (def *stepDef) run(ctx context.Context, t TestingT, step *messages.GherkinD
 	}
 
 	in := []reflect.Value{reflect.ValueOf(t), reflect.ValueOf(ctx)}
+
 	for i, v := range params {
 		if len(params) < i+1 {
 			break
 		}
+
 		inType := d.Type().In(i + 2)
 		paramType := paramType(v, inType)
 		in = append(in, paramType)
 	}
 
 	retValues := d.Call(in)
+
 	return retValues[0].Interface().(context.Context)
 }
 
@@ -408,26 +424,31 @@ func paramType(param []byte, inType reflect.Type) reflect.Value {
 	if inType.Kind() == reflect.String {
 		paramType = reflect.ValueOf(string(paramType.Interface().([]uint8)))
 	}
+
 	if inType.Kind() == reflect.Int {
 		s := paramType.Interface().([]uint8)
 		p, _ := strconv.Atoi(string(s))
 		paramType = reflect.ValueOf(p)
 	}
+
 	if inType.Kind() == reflect.Float32 {
 		s := paramType.Interface().([]uint8)
 		p, _ := strconv.ParseFloat(string(s), 32)
 		paramType = reflect.ValueOf(float32(p))
 	}
+
 	if inType.Kind() == reflect.Float64 {
 		s := paramType.Interface().([]uint8)
 		p, _ := strconv.ParseFloat(string(s), 32)
 		paramType = reflect.ValueOf(p)
 	}
+
 	return paramType
 }
 
 func (s *Suite) findStepDef(text string) (stepDef, error) {
 	var sd stepDef
+
 	found := 0
 
 	for _, step := range s.steps {
@@ -448,7 +469,7 @@ func (s *Suite) findStepDef(text string) (stepDef, error) {
 	return sd, nil
 }
 
-func (s *Suite) skipScenario(scenarioTags []*messages.GherkinDocument_Feature_Tag) bool {
+func (s *Suite) skipScenario(scenarioTags []*msgs.GherkinDocument_Feature_Tag) bool {
 	for _, tag := range scenarioTags {
 		if contains(s.options.ignoreTags, tag.Name) {
 			return true
@@ -468,7 +489,7 @@ func (s *Suite) skipScenario(scenarioTags []*messages.GherkinDocument_Feature_Ta
 	return true
 }
 
-func (s *Suite) getBackgroundSteps(bkg *messages.GherkinDocument_Feature_Background) []*messages.GherkinDocument_Feature_Step {
+func (s *Suite) getBackgroundSteps(bkg *msgs.GherkinDocument_Feature_Background) []*msgs.GherkinDocument_Feature_Step {
 	return bkg.Steps
 }
 
@@ -479,6 +500,7 @@ func contains(a []string, x string) bool {
 			return true
 		}
 	}
+
 	return false
 }
 
diff --git a/steps.go b/steps.go
index e327815..bb8e178 100644
--- a/steps.go
+++ b/steps.go
@@ -16,7 +16,9 @@ func validateStepFunc(f interface{}) error {
 	if value.Type().NumOut() != 1 {
 		return errors.New("the function should return the context.Context")
 	}
+
 	val := value.Type().Out(0)
+
 	n := val.ConvertibleTo(reflect.TypeOf((*context.Context)(nil)).Elem())
 	if !n {
 		return errors.New("the returned value should implement the context.Context interface")
@@ -27,15 +29,18 @@ func validateStepFunc(f interface{}) error {
 	}
 
 	val = value.Type().In(0)
+
 	testingInterface := reflect.TypeOf((*StepTest)(nil)).Elem()
 	if !val.Implements(testingInterface) {
 		return errors.New("the function should have the StepTest as the first argument")
 	}
 
 	val = value.Type().In(1)
+
 	n = val.ConvertibleTo(reflect.TypeOf((*context.Context)(nil)).Elem())
 	if !n {
 		return errors.New("the function should have Context as the second argument")
 	}
+
 	return nil
 }


Test patch:
diff --git a/gobdd_test.go b/gobdd_test.go
index 557ceb5..485ef87 100644
--- a/gobdd_test.go
+++ b/gobdd_test.go
@@ -6,16 +6,15 @@ import (
 	"regexp"
 	"testing"
 
-	"github.com/cucumber/messages-go/v9"
 	"github.com/go-bdd/assert"
 	"github.com/go-bdd/gobdd/context"
 )
 
 func TestScenarios(t *testing.T) {
 	suite := NewSuite(t, WithFeaturesPath("features/example.feature"))
-	compiled, _ := regexp.Compile(`I add (\d+) and (\d+)`)
+	compiled := regexp.MustCompile(`I add (\d+) and (\d+)`)
 	suite.AddRegexStep(compiled, add)
-	compiled, _ = regexp.Compile(`the result should equal (\d+)`)
+	compiled = regexp.MustCompile(`the result should equal (\d+)`)
 	suite.AddRegexStep(compiled, check)
 
 	suite.Run()
@@ -106,10 +105,9 @@ func TestFailureOutput(t *testing.T) {
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
 			def := stepDef{f: testCase.f}
-			step := &messages.GherkinDocument_Feature_Step{Keyword: "Test", Text: "text"}
 
 			tester := &mockTester{}
-			def.run(context.New(), tester, step, nil)
+			def.run(context.New(), tester, nil)
 			err := assert.Equals(testCase.expectedErrors, tester.errors)
 			if err != nil {
 				t.Fatal(err)
@@ -121,12 +119,14 @@ func TestFailureOutput(t *testing.T) {
 func addf(t StepTest, ctx context.Context, var1, var2 float32) context.Context {
 	res := var1 + var2
 	ctx.Set("sumRes", res)
+
 	return ctx
 }
 
 func add(t StepTest, ctx context.Context, var1, var2 int) context.Context {
 	res := var1 + var2
 	ctx.Set("sumRes", res)
+
 	return ctx
 }