From 4a754b26a5c1fd9c15543f7273810c38ab2f1f90 Mon Sep 17 00:00:00 2001 From: Matthew Rothenberg Date: Mon, 24 Apr 2017 21:10:55 -0400 Subject: [PATCH] allow randomizing scenario order --- options.go | 7 +++++++ run.go | 12 ++++++++++++ suite.go | 17 ++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/options.go b/options.go index 29a6070..5858428 100644 --- a/options.go +++ b/options.go @@ -13,6 +13,13 @@ type Options struct { // Print step definitions found and exit ShowStepDefinitions bool + // Run scenarios in random order. + // + // This is especially helpful for detecting situations + // where you have state leaking between scenarios, which + // can cause flickering or fragile tests. + RandomOrder bool + // Stops on the first failure StopOnFailure bool diff --git a/run.go b/run.go index cb73447..50f93b2 100644 --- a/run.go +++ b/run.go @@ -3,7 +3,9 @@ package godog import ( "fmt" "io" + "math/rand" "os" + "time" "github.com/DATA-DOG/godog/colors" ) @@ -11,6 +13,7 @@ import ( type initializer func(*Suite) type runner struct { + randomOrder bool stopOnFailure bool features []*feature fmt Formatter @@ -30,6 +33,7 @@ func (r *runner) concurrent(rate int) (failed bool) { } suite := &Suite{ fmt: r.fmt, + randomOrder: r.randomOrder, stopOnFailure: r.stopOnFailure, features: []*feature{feat}, } @@ -54,6 +58,7 @@ func (r *runner) concurrent(rate int) (failed bool) { func (r *runner) run() (failed bool) { suite := &Suite{ fmt: r.fmt, + randomOrder: r.randomOrder, stopOnFailure: r.stopOnFailure, features: r.features, } @@ -110,9 +115,16 @@ func RunWithOptions(suite string, contextInitializer func(suite *Suite), opt Opt fmt: formatter(suite, output), initializer: contextInitializer, features: features, + randomOrder: opt.RandomOrder, stopOnFailure: opt.StopOnFailure, } + if opt.RandomOrder { + // TODO(mroth): allow for seed to be specified in options, + // and print it at the end of a run for replication purposes + rand.Seed(time.Now().UTC().UnixNano()) + } + var failed bool if opt.Concurrency > 1 { failed = r.concurrent(opt.Concurrency) diff --git a/suite.go b/suite.go index 55fd057..18cd7f8 100644 --- a/suite.go +++ b/suite.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "math/rand" "os" "path/filepath" "reflect" @@ -49,6 +50,7 @@ type Suite struct { fmt Formatter failed bool + randomOrder bool stopOnFailure bool // suite event handlers @@ -330,7 +332,20 @@ func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Backgrou func (s *Suite) runFeature(f *feature) { s.fmt.Feature(f.Feature, f.Path, f.Content) - for _, scenario := range f.ScenarioDefinitions { + + // make a local copy of the feature scenario defenitions, + // then shuffle it if we are randomizing scenarios + scenarios := make([]interface{}, len(f.ScenarioDefinitions)) + if s.randomOrder { + perm := rand.Perm(len(f.ScenarioDefinitions)) + for i, v := range perm { + scenarios[v] = f.ScenarioDefinitions[i] + } + } else { + copy(scenarios, f.ScenarioDefinitions) + } + + for _, scenario := range scenarios { var err error if f.Background != nil { s.fmt.Node(f.Background)