Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nested steps #639

Open
vec715 opened this issue Aug 9, 2024 · 2 comments
Open

Support nested steps #639

vec715 opened this issue Aug 9, 2024 · 2 comments

Comments

@vec715
Copy link

vec715 commented Aug 9, 2024

🤔 What's the problem you're trying to solve?

The current flat design of Godog tests makes the structure difficult to read and navigate. Unlike Goconvey, Godog doesn't natively support nested steps, which would improve readability and allow for easier reuse of variables from parent functions. This limitation makes writing and maintaining tests more challenging and time-consuming

✨ What's your proposed solution?

Implement a feature in Godog that allows for nested steps, similar to Goconvey's approach. This would involve:

  • Creating a hierarchical structure for test steps.
  • Allowing variables to be shared between parent and child steps.

As an initial attempt, I created a custom ScenarioBuilder struct to simulate nested steps:

package features

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

var opts = godog.Options{
	Output: colors.Colored(os.Stdout),
	Format: "progress", // can define default values
}

func init() {
	godog.BindCommandLineFlags("godog.", &opts) // godog v0.11.0 and later
}

type ScenarioBuilder struct {
	name string
	ctx  *godog.ScenarioContext
}

func NewScenario(name string, ctx *godog.ScenarioContext) *ScenarioBuilder {
	return &ScenarioBuilder{name: name, ctx: ctx}
}

func (s *ScenarioBuilder) Given(expr string, f interface{}) *ScenarioBuilder {
	s.ctx.Given(`^`+expr+`$`, f)
	return s
}

func (s *ScenarioBuilder) When(expr string, f interface{}) *ScenarioBuilder {
	s.ctx.When(`^`+expr+`$`, f)
	return s
}

func (s *ScenarioBuilder) Then(expr string, f interface{}) *ScenarioBuilder {
	s.ctx.Then(`^`+expr+`$`, f)
	return s
}

func (s *ScenarioBuilder) And(expr string, f interface{}) *ScenarioBuilder {
	s.ctx.Step(`^`+expr+`$`, f)
	return s
}

This approach initially worked
image

But (!) I encountered issues when more test cases were added.

⛏ Have you considered any alternatives or workarounds?

No response

📚 Any additional context?

No response

@nhv96
Copy link

nhv96 commented Oct 27, 2024

Hi, your approach is possible, but I think it will come with many limitations:

  • Readability: initially, the *.feature files are the place we will define the feature, scenario, steps, rules,... in Gherkin style, which is easily to read and understand by all members of a team (dev, QA, PM, stakeholders...). So with your approach, all the step definitions and implementations will be written in the same .go file or even if you separate them into callback functions, they will still be coupled with each others, and will prevent members other than the person who code it, can understand the feature thoroughly.
  • Maintainability: as the feature extends, we need to add more steps, your nested structure will have a lot of nested indents, and since everything is nested, meaning the parent function is expected to coupled or tied to its children as well as its own parent, this will also means we cannot reuse that callback function properly or modify them. It is very common that a step can be reused in many features and scenarios.
  • Variables sharing between steps: I've seen implementations using Godog that can separate each scenario/feature into a stand alone struct, with it's own context as well as sharing common variable using context.WithValue and each step can retrieve the variable from context or from the struct fields. Otherwise, the example of Godog readme is enough to give us ideas to address this need.

Even thought it is possible and also already be implemented in Goconvey, I think Godog should not follow the same, as we're using BDD, it leverages the collaboration of the whole team, the step definitions of .feature file plays a crucial role as well as it help us have a clear view on what the behavior should look like, we can break it down to smaller parts with ease and we don't need to worry about how underlying code is implemented.

@vearutop
Copy link
Member

I agree, godog is all about running gherkin files given step definitions. Replacing gherkin with go code moves it into a different problem space.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants