closes #97
Этот коммит содержится в:
родитель
aea8188613
коммит
db3c6246b8
4 изменённых файлов: 81 добавлений и 12 удалений
|
@ -303,6 +303,14 @@ corruption or race conditions in the application.
|
||||||
It is also useful to randomize the order of scenario execution, which you
|
It is also useful to randomize the order of scenario execution, which you
|
||||||
can now do with **--random** command option.
|
can now do with **--random** command option.
|
||||||
|
|
||||||
|
**NOTE:** if suite runs with concurrency option, it concurrently runs
|
||||||
|
every feature, not scenario per different features. This gives
|
||||||
|
a flexibility to isolate state per feature. For example using
|
||||||
|
**BeforeFeature** hook, it is possible to spin up costly service and shut
|
||||||
|
it down only in **AfterFeature** hook and share the service between all
|
||||||
|
scenarios in that feature. It is not advisable though, because you are
|
||||||
|
risking having a state dependency.
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
|
Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
|
||||||
|
|
|
@ -16,10 +16,12 @@ Feature: suite events
|
||||||
When I run feature suite
|
When I run feature suite
|
||||||
Then these events had to be fired for a number of times:
|
Then these events had to be fired for a number of times:
|
||||||
| BeforeSuite | 1 |
|
| BeforeSuite | 1 |
|
||||||
|
| BeforeFeature | 1 |
|
||||||
| BeforeScenario | 1 |
|
| BeforeScenario | 1 |
|
||||||
| BeforeStep | 3 |
|
| BeforeStep | 3 |
|
||||||
| AfterStep | 3 |
|
| AfterStep | 3 |
|
||||||
| AfterScenario | 1 |
|
| AfterScenario | 1 |
|
||||||
|
| AfterFeature | 1 |
|
||||||
| AfterSuite | 1 |
|
| AfterSuite | 1 |
|
||||||
|
|
||||||
Scenario: triggers appropriate events whole feature
|
Scenario: triggers appropriate events whole feature
|
||||||
|
@ -27,8 +29,25 @@ Feature: suite events
|
||||||
When I run feature suite
|
When I run feature suite
|
||||||
Then these events had to be fired for a number of times:
|
Then these events had to be fired for a number of times:
|
||||||
| BeforeSuite | 1 |
|
| BeforeSuite | 1 |
|
||||||
|
| BeforeFeature | 1 |
|
||||||
| BeforeScenario | 6 |
|
| BeforeScenario | 6 |
|
||||||
| BeforeStep | 19 |
|
| BeforeStep | 19 |
|
||||||
| AfterStep | 19 |
|
| AfterStep | 19 |
|
||||||
| AfterScenario | 6 |
|
| AfterScenario | 6 |
|
||||||
|
| AfterFeature | 1 |
|
||||||
| AfterSuite | 1 |
|
| AfterSuite | 1 |
|
||||||
|
|
||||||
|
Scenario: triggers appropriate events for two feature files
|
||||||
|
Given a feature path "features/load.feature:6"
|
||||||
|
And a feature path "features/multistep.feature:6"
|
||||||
|
When I run feature suite
|
||||||
|
Then these events had to be fired for a number of times:
|
||||||
|
| BeforeSuite | 1 |
|
||||||
|
| BeforeFeature | 2 |
|
||||||
|
| BeforeScenario | 2 |
|
||||||
|
| BeforeStep | 7 |
|
||||||
|
| AfterStep | 7 |
|
||||||
|
| AfterScenario | 2 |
|
||||||
|
| AfterFeature | 2 |
|
||||||
|
| AfterSuite | 1 |
|
||||||
|
|
||||||
|
|
60
suite.go
60
suite.go
|
@ -55,10 +55,12 @@ type Suite struct {
|
||||||
|
|
||||||
// suite event handlers
|
// suite event handlers
|
||||||
beforeSuiteHandlers []func()
|
beforeSuiteHandlers []func()
|
||||||
|
beforeFeatureHandlers []func(*gherkin.Feature)
|
||||||
beforeScenarioHandlers []func(interface{})
|
beforeScenarioHandlers []func(interface{})
|
||||||
beforeStepHandlers []func(*gherkin.Step)
|
beforeStepHandlers []func(*gherkin.Step)
|
||||||
afterStepHandlers []func(*gherkin.Step, error)
|
afterStepHandlers []func(*gherkin.Step, error)
|
||||||
afterScenarioHandlers []func(interface{}, error)
|
afterScenarioHandlers []func(interface{}, error)
|
||||||
|
afterFeatureHandlers []func(*gherkin.Feature)
|
||||||
afterSuiteHandlers []func()
|
afterSuiteHandlers []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,8 +132,26 @@ func (s *Suite) Step(expr interface{}, stepFunc interface{}) {
|
||||||
//
|
//
|
||||||
// Use it to prepare the test suite for a spin.
|
// Use it to prepare the test suite for a spin.
|
||||||
// Connect and prepare database for instance...
|
// Connect and prepare database for instance...
|
||||||
func (s *Suite) BeforeSuite(f func()) {
|
func (s *Suite) BeforeSuite(fn func()) {
|
||||||
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, f)
|
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeFeature registers a function or method
|
||||||
|
// to be run once before every feature execution.
|
||||||
|
//
|
||||||
|
// If godog is run with concurrency option, it will
|
||||||
|
// run every feature per goroutine. So user may choose
|
||||||
|
// whether to isolate state within feature context or
|
||||||
|
// scenario.
|
||||||
|
//
|
||||||
|
// Best practice is not to have any state dependency on
|
||||||
|
// every scenario, but in some cases if VM for example
|
||||||
|
// needs to be started it may take very long for each
|
||||||
|
// scenario to restart it.
|
||||||
|
//
|
||||||
|
// Use it wisely and avoid sharing state between scenarios.
|
||||||
|
func (s *Suite) BeforeFeature(fn func(*gherkin.Feature)) {
|
||||||
|
s.beforeFeatureHandlers = append(s.beforeFeatureHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeScenario registers a function or method
|
// BeforeScenario registers a function or method
|
||||||
|
@ -143,14 +163,14 @@ func (s *Suite) BeforeSuite(f func()) {
|
||||||
// It is a good practice to restore the default state
|
// 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.
|
// any kind of state.
|
||||||
func (s *Suite) BeforeScenario(f func(interface{})) {
|
func (s *Suite) BeforeScenario(fn func(interface{})) {
|
||||||
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, f)
|
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeStep registers a function or method
|
// BeforeStep registers a function or method
|
||||||
// to be run before every scenario
|
// to be run before every scenario
|
||||||
func (s *Suite) BeforeStep(f func(*gherkin.Step)) {
|
func (s *Suite) BeforeStep(fn func(*gherkin.Step)) {
|
||||||
s.beforeStepHandlers = append(s.beforeStepHandlers, f)
|
s.beforeStepHandlers = append(s.beforeStepHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterStep registers an function or method
|
// AfterStep registers an function or method
|
||||||
|
@ -162,8 +182,8 @@ func (s *Suite) BeforeStep(f func(*gherkin.Step)) {
|
||||||
//
|
//
|
||||||
// In some cases, for example when running a headless
|
// In some cases, for example when running a headless
|
||||||
// browser, to take a screenshot after failure.
|
// browser, to take a screenshot after failure.
|
||||||
func (s *Suite) AfterStep(f func(*gherkin.Step, error)) {
|
func (s *Suite) AfterStep(fn func(*gherkin.Step, error)) {
|
||||||
s.afterStepHandlers = append(s.afterStepHandlers, f)
|
s.afterStepHandlers = append(s.afterStepHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterScenario registers an function or method
|
// AfterScenario registers an function or method
|
||||||
|
@ -171,14 +191,20 @@ func (s *Suite) AfterStep(f func(*gherkin.Step, error)) {
|
||||||
//
|
//
|
||||||
// The interface argument may be *gherkin.Scenario
|
// The interface argument may be *gherkin.Scenario
|
||||||
// or *gherkin.ScenarioOutline
|
// or *gherkin.ScenarioOutline
|
||||||
func (s *Suite) AfterScenario(f func(interface{}, error)) {
|
func (s *Suite) AfterScenario(fn func(interface{}, error)) {
|
||||||
s.afterScenarioHandlers = append(s.afterScenarioHandlers, f)
|
s.afterScenarioHandlers = append(s.afterScenarioHandlers, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterFeature registers a function or method
|
||||||
|
// to be run once after feature executed all scenarios.
|
||||||
|
func (s *Suite) AfterFeature(fn func(*gherkin.Feature)) {
|
||||||
|
s.afterFeatureHandlers = append(s.afterFeatureHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterSuite registers a function or method
|
// AfterSuite registers a function or method
|
||||||
// to be run once after suite runner
|
// to be run once after suite runner
|
||||||
func (s *Suite) AfterSuite(f func()) {
|
func (s *Suite) AfterSuite(fn func()) {
|
||||||
s.afterSuiteHandlers = append(s.afterSuiteHandlers, f)
|
s.afterSuiteHandlers = append(s.afterSuiteHandlers, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) run() {
|
func (s *Suite) run() {
|
||||||
|
@ -492,6 +518,10 @@ func (s *Suite) shouldFail(err error) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) runFeature(f *feature) {
|
func (s *Suite) runFeature(f *feature) {
|
||||||
|
for _, fn := range s.beforeFeatureHandlers {
|
||||||
|
fn(f.Feature)
|
||||||
|
}
|
||||||
|
|
||||||
s.fmt.Feature(f.Feature, f.Path, f.Content)
|
s.fmt.Feature(f.Feature, f.Path, f.Content)
|
||||||
|
|
||||||
// make a local copy of the feature scenario defenitions,
|
// make a local copy of the feature scenario defenitions,
|
||||||
|
@ -507,6 +537,12 @@ func (s *Suite) runFeature(f *feature) {
|
||||||
copy(scenarios, f.ScenarioDefinitions)
|
copy(scenarios, f.ScenarioDefinitions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
for _, fn := range s.afterFeatureHandlers {
|
||||||
|
fn(f.Feature)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
var err error
|
var err error
|
||||||
if f.Background != nil {
|
if f.Background != nil {
|
||||||
|
|
|
@ -275,6 +275,12 @@ func (s *suiteContext) iAmListeningToSuiteEvents() error {
|
||||||
s.testedSuite.AfterSuite(func() {
|
s.testedSuite.AfterSuite(func() {
|
||||||
s.events = append(s.events, &firedEvent{"AfterSuite", []interface{}{}})
|
s.events = append(s.events, &firedEvent{"AfterSuite", []interface{}{}})
|
||||||
})
|
})
|
||||||
|
s.testedSuite.BeforeFeature(func(ft *gherkin.Feature) {
|
||||||
|
s.events = append(s.events, &firedEvent{"BeforeFeature", []interface{}{ft}})
|
||||||
|
})
|
||||||
|
s.testedSuite.AfterFeature(func(ft *gherkin.Feature) {
|
||||||
|
s.events = append(s.events, &firedEvent{"AfterFeature", []interface{}{ft}})
|
||||||
|
})
|
||||||
s.testedSuite.BeforeScenario(func(scenario interface{}) {
|
s.testedSuite.BeforeScenario(func(scenario interface{}) {
|
||||||
s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{scenario}})
|
s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{scenario}})
|
||||||
})
|
})
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче