Этот коммит содержится в:
gedi 2017-08-31 09:30:17 +03:00
родитель aea8188613
коммит db3c6246b8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 56604CDCCC201556
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 |

Просмотреть файл

@ -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}})
}) })