Этот коммит содержится в:
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
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
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
Then these events had to be fired for a number of times:
| BeforeSuite | 1 |
| BeforeFeature | 1 |
| BeforeScenario | 1 |
| BeforeStep | 3 |
| AfterStep | 3 |
| AfterScenario | 1 |
| AfterFeature | 1 |
| AfterSuite | 1 |
Scenario: triggers appropriate events whole feature
@ -27,8 +29,25 @@ Feature: suite events
When I run feature suite
Then these events had to be fired for a number of times:
| BeforeSuite | 1 |
| BeforeFeature | 1 |
| BeforeScenario | 6 |
| BeforeStep | 19 |
| AfterStep | 19 |
| AfterScenario | 6 |
| AfterFeature | 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
beforeSuiteHandlers []func()
beforeFeatureHandlers []func(*gherkin.Feature)
beforeScenarioHandlers []func(interface{})
beforeStepHandlers []func(*gherkin.Step)
afterStepHandlers []func(*gherkin.Step, error)
afterScenarioHandlers []func(interface{}, error)
afterFeatureHandlers []func(*gherkin.Feature)
afterSuiteHandlers []func()
}
@ -130,8 +132,26 @@ func (s *Suite) Step(expr interface{}, stepFunc interface{}) {
//
// Use it to prepare the test suite for a spin.
// Connect and prepare database for instance...
func (s *Suite) BeforeSuite(f func()) {
s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, f)
func (s *Suite) BeforeSuite(fn func()) {
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
@ -143,14 +163,14 @@ func (s *Suite) BeforeSuite(f func()) {
// It is a good practice to restore the default state
// before every scenario so it would be isolated from
// any kind of state.
func (s *Suite) BeforeScenario(f func(interface{})) {
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, f)
func (s *Suite) BeforeScenario(fn func(interface{})) {
s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, fn)
}
// BeforeStep registers a function or method
// to be run before every scenario
func (s *Suite) BeforeStep(f func(*gherkin.Step)) {
s.beforeStepHandlers = append(s.beforeStepHandlers, f)
func (s *Suite) BeforeStep(fn func(*gherkin.Step)) {
s.beforeStepHandlers = append(s.beforeStepHandlers, fn)
}
// 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
// browser, to take a screenshot after failure.
func (s *Suite) AfterStep(f func(*gherkin.Step, error)) {
s.afterStepHandlers = append(s.afterStepHandlers, f)
func (s *Suite) AfterStep(fn func(*gherkin.Step, error)) {
s.afterStepHandlers = append(s.afterStepHandlers, fn)
}
// 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
// or *gherkin.ScenarioOutline
func (s *Suite) AfterScenario(f func(interface{}, error)) {
s.afterScenarioHandlers = append(s.afterScenarioHandlers, f)
func (s *Suite) AfterScenario(fn func(interface{}, error)) {
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
// to be run once after suite runner
func (s *Suite) AfterSuite(f func()) {
s.afterSuiteHandlers = append(s.afterSuiteHandlers, f)
func (s *Suite) AfterSuite(fn func()) {
s.afterSuiteHandlers = append(s.afterSuiteHandlers, fn)
}
func (s *Suite) run() {
@ -492,6 +518,10 @@ func (s *Suite) shouldFail(err error) bool {
}
func (s *Suite) runFeature(f *feature) {
for _, fn := range s.beforeFeatureHandlers {
fn(f.Feature)
}
s.fmt.Feature(f.Feature, f.Path, f.Content)
// make a local copy of the feature scenario defenitions,
@ -507,6 +537,12 @@ func (s *Suite) runFeature(f *feature) {
copy(scenarios, f.ScenarioDefinitions)
}
defer func() {
for _, fn := range s.afterFeatureHandlers {
fn(f.Feature)
}
}()
for _, scenario := range scenarios {
var err error
if f.Background != nil {

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

@ -275,6 +275,12 @@ func (s *suiteContext) iAmListeningToSuiteEvents() error {
s.testedSuite.AfterSuite(func() {
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.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{scenario}})
})