Allow suite-level configuration of steps and hooks (#453)
* Allow suite-level configuration of steps and hooks * Fix a few typos * Update CHANGELOG.md * Add test * Run scenario in same goroutine to preserve stack when concurrency is disabled * Remove redundant check
Этот коммит содержится в:
		
							родитель
							
								
									a6fef3f171
								
							
						
					
					
						коммит
						9baac0fdfa
					
				
					 4 изменённых файлов: 67 добавлений и 19 удалений
				
			
		
							
								
								
									
										11
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -8,6 +8,17 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt | |||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ## [Unreleased] | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Allow suite-level configuration of steps and hooks ([453](https://github.com/cucumber/godog/pull/453) - [vearutop]) | ||||
| 
 | ||||
| ## Changed | ||||
| 
 | ||||
| - Run scenarios in the same goroutine if concurrency is disabled (([453](https://github.com/cucumber/godog/pull/453) - [vearutop])) | ||||
| 
 | ||||
| 
 | ||||
| ## [v0.12.3] | ||||
| 
 | ||||
| ### Added | ||||
|  |  | |||
							
								
								
									
										35
									
								
								run.go
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								run.go
									
										
									
									
									
								
							|  | @ -57,7 +57,16 @@ func (r *runner) concurrent(rate int) (failed bool) { | |||
| 		fmt.SetStorage(r.storage) | ||||
| 	} | ||||
| 
 | ||||
| 	testSuiteContext := TestSuiteContext{} | ||||
| 	testSuiteContext := TestSuiteContext{ | ||||
| 		suite: &suite{ | ||||
| 			fmt:            r.fmt, | ||||
| 			randomSeed:     r.randomSeed, | ||||
| 			strict:         r.strict, | ||||
| 			storage:        r.storage, | ||||
| 			defaultContext: r.defaultContext, | ||||
| 			testingT:       r.testingT, | ||||
| 		}, | ||||
| 	} | ||||
| 	if r.testSuiteInitializer != nil { | ||||
| 		r.testSuiteInitializer(&testSuiteContext) | ||||
| 	} | ||||
|  | @ -93,7 +102,7 @@ func (r *runner) concurrent(rate int) (failed bool) { | |||
| 				r.fmt.Feature(ft.GherkinDocument, ft.Uri, ft.Content) | ||||
| 			} | ||||
| 
 | ||||
| 			go func(fail *bool, pickle *messages.Pickle) { | ||||
| 			runPickle := func(fail *bool, pickle *messages.Pickle) { | ||||
| 				defer func() { | ||||
| 					<-queue // free a space in queue | ||||
| 				}() | ||||
|  | @ -102,17 +111,11 @@ func (r *runner) concurrent(rate int) (failed bool) { | |||
| 					return | ||||
| 				} | ||||
| 
 | ||||
| 				suite := &suite{ | ||||
| 					fmt:            r.fmt, | ||||
| 					randomSeed:     r.randomSeed, | ||||
| 					strict:         r.strict, | ||||
| 					storage:        r.storage, | ||||
| 					defaultContext: r.defaultContext, | ||||
| 					testingT:       r.testingT, | ||||
| 				} | ||||
| 				// Copy base suite. | ||||
| 				suite := *testSuiteContext.suite | ||||
| 
 | ||||
| 				if r.scenarioInitializer != nil { | ||||
| 					sc := ScenarioContext{suite: suite} | ||||
| 					sc := ScenarioContext{suite: &suite} | ||||
| 					r.scenarioInitializer(&sc) | ||||
| 				} | ||||
| 
 | ||||
|  | @ -122,7 +125,15 @@ func (r *runner) concurrent(rate int) (failed bool) { | |||
| 					*fail = true | ||||
| 					copyLock.Unlock() | ||||
| 				} | ||||
| 			}(&failed, &pickle) | ||||
| 			} | ||||
| 
 | ||||
| 			if rate == 1 { | ||||
| 				// Running within the same goroutine for concurrency 1 | ||||
| 				// to preserve original stacks and simplify debugging. | ||||
| 				runPickle(&failed, &pickle) | ||||
| 			} else { | ||||
| 				go runPickle(&failed, &pickle) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										18
									
								
								run_test.go
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								run_test.go
									
										
									
									
									
								
							|  | @ -2,6 +2,7 @@ package godog | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
|  | @ -77,9 +78,22 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { | |||
| 	ft := models.Feature{GherkinDocument: gd} | ||||
| 	ft.Pickles = gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) | ||||
| 
 | ||||
| 	var beforeScenarioFired, afterScenarioFired int | ||||
| 
 | ||||
| 	r := runner{ | ||||
| 		fmt:      formatters.ProgressFormatterFunc("progress", ioutil.Discard), | ||||
| 		features: []*models.Feature{&ft}, | ||||
| 		testSuiteInitializer: func(ctx *TestSuiteContext) { | ||||
| 			ctx.ScenarioContext().Before(func(ctx context.Context, sc *Scenario) (context.Context, error) { | ||||
| 				beforeScenarioFired++ | ||||
| 				return ctx, nil | ||||
| 			}) | ||||
| 
 | ||||
| 			ctx.ScenarioContext().After(func(ctx context.Context, sc *Scenario, err error) (context.Context, error) { | ||||
| 				afterScenarioFired++ | ||||
| 				return ctx, nil | ||||
| 			}) | ||||
| 		}, | ||||
| 		scenarioInitializer: func(ctx *ScenarioContext) { | ||||
| 			ctx.Step(`^one$`, func() error { return nil }) | ||||
| 			ctx.Step(`^two$`, func() error { return ErrPending }) | ||||
|  | @ -94,10 +108,14 @@ func Test_FailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { | |||
| 
 | ||||
| 	failed := r.concurrent(1) | ||||
| 	require.False(t, failed) | ||||
| 	assert.Equal(t, 1, beforeScenarioFired) | ||||
| 	assert.Equal(t, 1, afterScenarioFired) | ||||
| 
 | ||||
| 	r.strict = true | ||||
| 	failed = r.concurrent(1) | ||||
| 	require.True(t, failed) | ||||
| 	assert.Equal(t, 2, beforeScenarioFired) | ||||
| 	assert.Equal(t, 2, afterScenarioFired) | ||||
| } | ||||
| 
 | ||||
| func Test_FailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) { | ||||
|  |  | |||
|  | @ -6,11 +6,10 @@ import ( | |||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 
 | ||||
| 	"github.com/cucumber/messages-go/v16" | ||||
| 
 | ||||
| 	"github.com/cucumber/godog/formatters" | ||||
| 	"github.com/cucumber/godog/internal/builder" | ||||
| 	"github.com/cucumber/godog/internal/models" | ||||
| 	"github.com/cucumber/messages-go/v16" | ||||
| ) | ||||
| 
 | ||||
| // GherkinDocument represents gherkin document. | ||||
|  | @ -66,6 +65,8 @@ type Table = messages.PickleTable | |||
| type TestSuiteContext struct { | ||||
| 	beforeSuiteHandlers []func() | ||||
| 	afterSuiteHandlers  []func() | ||||
| 
 | ||||
| 	suite *suite | ||||
| } | ||||
| 
 | ||||
| // BeforeSuite registers a function or method | ||||
|  | @ -83,6 +84,13 @@ func (ctx *TestSuiteContext) AfterSuite(fn func()) { | |||
| 	ctx.afterSuiteHandlers = append(ctx.afterSuiteHandlers, fn) | ||||
| } | ||||
| 
 | ||||
| // ScenarioContext allows registering scenario hooks. | ||||
| func (ctx *TestSuiteContext) ScenarioContext() *ScenarioContext { | ||||
| 	return &ScenarioContext{ | ||||
| 		suite: ctx.suite, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ScenarioContext allows various contexts | ||||
| // to register steps and event handlers. | ||||
| // | ||||
|  | @ -103,11 +111,11 @@ type StepContext struct { | |||
| 	suite *suite | ||||
| } | ||||
| 
 | ||||
| // Before registers a a function or method | ||||
| // Before registers a function or method | ||||
| // to be run before every scenario. | ||||
| // | ||||
| // It is a good practice to restore the default state | ||||
| // before every scenario so it would be isolated from | ||||
| // before every scenario, so it would be isolated from | ||||
| // any kind of state. | ||||
| func (ctx ScenarioContext) Before(h BeforeScenarioHook) { | ||||
| 	ctx.suite.beforeScenarioHandlers = append(ctx.suite.beforeScenarioHandlers, h) | ||||
|  | @ -139,7 +147,7 @@ func (ctx StepContext) Before(h BeforeStepHook) { | |||
| // BeforeStepHook defines a hook before step. | ||||
| type BeforeStepHook func(ctx context.Context, st *Step) (context.Context, error) | ||||
| 
 | ||||
| // After registers an function or method | ||||
| // After registers a function or method | ||||
| // to be run after every step. | ||||
| // | ||||
| // It may be convenient to return a different kind of error | ||||
|  | @ -171,7 +179,7 @@ func (ctx *ScenarioContext) BeforeScenario(fn func(sc *Scenario)) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // AfterScenario registers an function or method | ||||
| // AfterScenario registers a function or method | ||||
| // to be run after every scenario. | ||||
| // | ||||
| // Deprecated: use After. | ||||
|  | @ -195,7 +203,7 @@ func (ctx *ScenarioContext) BeforeStep(fn func(st *Step)) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // AfterStep registers an function or method | ||||
| // AfterStep registers a function or method | ||||
| // to be run after every step. | ||||
| // | ||||
| // It may be convenient to return a different kind of error | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Viacheslav Poturaev
						Viacheslav Poturaev