add suite events and possibility to listen to them
Этот коммит содержится в:
		
							родитель
							
								
									0f6ec04318
								
							
						
					
					
						коммит
						00fab00e22
					
				
					 5 изменённых файлов: 289 добавлений и 62 удалений
				
			
		|  | @ -13,6 +13,13 @@ type Arg struct { | |||
| 	value interface{} | ||||
| } | ||||
| 
 | ||||
| // StepArgument func creates a step argument. | ||||
| // used in cases when calling another step from | ||||
| // within a StepHandlerFunc | ||||
| func StepArgument(value interface{}) *Arg { | ||||
| 	return &Arg{value: value} | ||||
| } | ||||
| 
 | ||||
| // Float64 converts an argument to float64 | ||||
| // or panics if unable to convert it | ||||
| func (a *Arg) Float64() float64 { | ||||
|  |  | |||
							
								
								
									
										103
									
								
								events.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										103
									
								
								events.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,103 @@ | |||
| package godog | ||||
| 
 | ||||
| import "github.com/DATA-DOG/godog/gherkin" | ||||
| 
 | ||||
| // BeforeSuiteHandler can be registered | ||||
| // in Suite to be executed once before | ||||
| // running a feature suite | ||||
| type BeforeSuiteHandler interface { | ||||
| 	HandleBeforeSuite() | ||||
| } | ||||
| 
 | ||||
| // BeforeSuiteHandlerFunc is a function implementing | ||||
| // BeforeSuiteHandler interface | ||||
| type BeforeSuiteHandlerFunc func() | ||||
| 
 | ||||
| // HandleBeforeSuite is called once before suite | ||||
| func (f BeforeSuiteHandlerFunc) HandleBeforeSuite() { | ||||
| 	f() | ||||
| } | ||||
| 
 | ||||
| // BeforeScenarioHandler can be registered | ||||
| // in Suite to be executed before every scenario | ||||
| // which will be run | ||||
| type BeforeScenarioHandler interface { | ||||
| 	HandleBeforeScenario(scenario *gherkin.Scenario) | ||||
| } | ||||
| 
 | ||||
| // BeforeScenarioHandlerFunc is a function implementing | ||||
| // BeforeScenarioHandler interface | ||||
| type BeforeScenarioHandlerFunc func(scenario *gherkin.Scenario) | ||||
| 
 | ||||
| // HandleBeforeScenario is called with a *gherkin.Scenario argument | ||||
| // for before every scenario which is run by suite | ||||
| func (f BeforeScenarioHandlerFunc) HandleBeforeScenario(scenario *gherkin.Scenario) { | ||||
| 	f(scenario) | ||||
| } | ||||
| 
 | ||||
| // BeforeStepHandler can be registered | ||||
| // in Suite to be executed before every step | ||||
| // which will be run | ||||
| type BeforeStepHandler interface { | ||||
| 	HandleBeforeStep(step *gherkin.Step) | ||||
| } | ||||
| 
 | ||||
| // BeforeStepHandlerFunc is a function implementing | ||||
| // BeforeStepHandler interface | ||||
| type BeforeStepHandlerFunc func(step *gherkin.Step) | ||||
| 
 | ||||
| // HandleBeforeStep is called with a *gherkin.Step argument | ||||
| // for before every step which is run by suite | ||||
| func (f BeforeStepHandlerFunc) HandleBeforeStep(step *gherkin.Step) { | ||||
| 	f(step) | ||||
| } | ||||
| 
 | ||||
| // AfterStepHandler can be registered | ||||
| // in Suite to be executed after every step | ||||
| // which will be run | ||||
| type AfterStepHandler interface { | ||||
| 	HandleAfterStep(step *gherkin.Step, status Status) | ||||
| } | ||||
| 
 | ||||
| // AfterStepHandlerFunc is a function implementing | ||||
| // AfterStepHandler interface | ||||
| type AfterStepHandlerFunc func(step *gherkin.Step, status Status) | ||||
| 
 | ||||
| // HandleAfterStep is called with a *gherkin.Step argument | ||||
| // for after every step which is run by suite | ||||
| func (f AfterStepHandlerFunc) HandleAfterStep(step *gherkin.Step, status Status) { | ||||
| 	f(step, status) | ||||
| } | ||||
| 
 | ||||
| // AfterScenarioHandler can be registered | ||||
| // in Suite to be executed after every scenario | ||||
| // which will be run | ||||
| type AfterScenarioHandler interface { | ||||
| 	HandleAfterScenario(scenario *gherkin.Scenario, status Status) | ||||
| } | ||||
| 
 | ||||
| // AfterScenarioHandlerFunc is a function implementing | ||||
| // AfterScenarioHandler interface | ||||
| type AfterScenarioHandlerFunc func(scenario *gherkin.Scenario, status Status) | ||||
| 
 | ||||
| // HandleAfterScenario is called with a *gherkin.Scenario argument | ||||
| // for after every scenario which is run by suite | ||||
| func (f AfterScenarioHandlerFunc) HandleAfterScenario(scenario *gherkin.Scenario, status Status) { | ||||
| 	f(scenario, status) | ||||
| } | ||||
| 
 | ||||
| // AfterSuiteHandler can be registered | ||||
| // in Suite to be executed once after | ||||
| // running a feature suite | ||||
| type AfterSuiteHandler interface { | ||||
| 	HandleAfterSuite() | ||||
| } | ||||
| 
 | ||||
| // AfterSuiteHandlerFunc is a function implementing | ||||
| // AfterSuiteHandler interface | ||||
| type AfterSuiteHandlerFunc func() | ||||
| 
 | ||||
| // HandleAfterSuite is called once after suite | ||||
| func (f AfterSuiteHandlerFunc) HandleAfterSuite() { | ||||
| 	f() | ||||
| } | ||||
|  | @ -4,10 +4,9 @@ Feature: suite hooks | |||
|   I need to provide a way to hook into these events | ||||
| 
 | ||||
|   Background: | ||||
|     Given I have a before scenario hook | ||||
|     And a feature path "features/load_features.feature:6" | ||||
|     And I parse features | ||||
|     Given I'm listening to suite events | ||||
| 
 | ||||
|   Scenario: triggers before scenario hook | ||||
|     When I run features | ||||
|     Then I should have a scenario "load features within path" recorded in the hook | ||||
|     Given a feature path "features/load_features.feature:6" | ||||
|     When I run feature suite | ||||
|     Then there was event triggered before scenario "load features within path" | ||||
|  |  | |||
							
								
								
									
										146
									
								
								suite.go
									
										
									
									
									
								
							
							
						
						
									
										146
									
								
								suite.go
									
										
									
									
									
								
							|  | @ -11,22 +11,27 @@ import ( | |||
| 	"github.com/DATA-DOG/godog/gherkin" | ||||
| ) | ||||
| 
 | ||||
| type stepsStatus int | ||||
| // Status represents a step status | ||||
| type Status int | ||||
| 
 | ||||
| const ( | ||||
| 	stepsStatusPassed stepsStatus = iota | ||||
| 	stepsStatusFailed | ||||
| 	stepsStatusUndefined | ||||
| 	invalid Status = iota | ||||
| 	Passed | ||||
| 	Failed | ||||
| 	Undefined | ||||
| ) | ||||
| 
 | ||||
| type BeforeScenarioHandler interface { | ||||
| 	BeforeScenario(scenario *gherkin.Scenario) | ||||
| } | ||||
| 
 | ||||
| type BeforeScenarioHandlerFunc func(scenario *gherkin.Scenario) | ||||
| 
 | ||||
| func (f BeforeScenarioHandlerFunc) BeforeScenario(scenario *gherkin.Scenario) { | ||||
| 	f(scenario) | ||||
| // String represents status as string | ||||
| func (s Status) String() string { | ||||
| 	switch s { | ||||
| 	case Passed: | ||||
| 		return "passed" | ||||
| 	case Failed: | ||||
| 		return "failed" | ||||
| 	case Undefined: | ||||
| 		return "undefined" | ||||
| 	} | ||||
| 	return "invalid" | ||||
| } | ||||
| 
 | ||||
| // Objects implementing the StepHandler interface can be | ||||
|  | @ -66,16 +71,29 @@ type stepMatchHandler struct { | |||
| // to register step definitions and event handlers | ||||
| type Suite interface { | ||||
| 	Step(expr *regexp.Regexp, h StepHandler) | ||||
| 	// suite events | ||||
| 	BeforeSuite(h BeforeSuiteHandler) | ||||
| 	BeforeScenario(h BeforeScenarioHandler) | ||||
| 	BeforeStep(h BeforeStepHandler) | ||||
| 	AfterStep(h AfterStepHandler) | ||||
| 	AfterScenario(h AfterScenarioHandler) | ||||
| 	AfterSuite(h AfterSuiteHandler) | ||||
| } | ||||
| 
 | ||||
| type suite struct { | ||||
| 	beforeScenarioHandlers []BeforeScenarioHandler | ||||
| 	stepHandlers           []*stepMatchHandler | ||||
| 	features               []*gherkin.Feature | ||||
| 	fmt                    Formatter | ||||
| 	stepHandlers []*stepMatchHandler | ||||
| 	features     []*gherkin.Feature | ||||
| 	fmt          Formatter | ||||
| 
 | ||||
| 	failed bool | ||||
| 
 | ||||
| 	// suite event handlers | ||||
| 	beforeSuiteHandlers    []BeforeSuiteHandler | ||||
| 	beforeScenarioHandlers []BeforeScenarioHandler | ||||
| 	beforeStepHandlers     []BeforeStepHandler | ||||
| 	afterStepHandlers      []AfterStepHandler | ||||
| 	afterScenarioHandlers  []AfterScenarioHandler | ||||
| 	afterSuiteHandlers     []AfterSuiteHandler | ||||
| } | ||||
| 
 | ||||
| // New initializes a suite which supports the Suite | ||||
|  | @ -102,10 +120,42 @@ func (s *suite) Step(expr *regexp.Regexp, h StepHandler) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // BeforeSuite registers a BeforeSuiteHandler | ||||
| // to be run once before suite runner | ||||
| func (s *suite) BeforeSuite(h BeforeSuiteHandler) { | ||||
| 	s.beforeSuiteHandlers = append(s.beforeSuiteHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // BeforeScenario registers a BeforeScenarioHandler | ||||
| // to be run before every scenario | ||||
| func (s *suite) BeforeScenario(h BeforeScenarioHandler) { | ||||
| 	s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // BeforeStep registers a BeforeStepHandler | ||||
| // to be run before every scenario | ||||
| func (s *suite) BeforeStep(h BeforeStepHandler) { | ||||
| 	s.beforeStepHandlers = append(s.beforeStepHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // AfterStep registers an AfterStepHandler | ||||
| // to be run after every scenario | ||||
| func (s *suite) AfterStep(h AfterStepHandler) { | ||||
| 	s.afterStepHandlers = append(s.afterStepHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // AfterScenario registers an AfterScenarioHandler | ||||
| // to be run after every scenario | ||||
| func (s *suite) AfterScenario(h AfterScenarioHandler) { | ||||
| 	s.afterScenarioHandlers = append(s.afterScenarioHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // AfterSuite registers a AfterSuiteHandler | ||||
| // to be run once after suite runner | ||||
| func (s *suite) AfterSuite(h AfterSuiteHandler) { | ||||
| 	s.afterSuiteHandlers = append(s.afterSuiteHandlers, h) | ||||
| } | ||||
| 
 | ||||
| // Run - runs a godog feature suite | ||||
| func (s *suite) Run() { | ||||
| 	var err error | ||||
|  | @ -129,6 +179,19 @@ func (s *suite) Run() { | |||
| 	s.features, err = cfg.features() | ||||
| 	fatal(err) | ||||
| 
 | ||||
| 	s.run() | ||||
| 
 | ||||
| 	if s.failed { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *suite) run() { | ||||
| 	// run before suite handlers | ||||
| 	for _, h := range s.beforeSuiteHandlers { | ||||
| 		h.HandleBeforeSuite() | ||||
| 	} | ||||
| 	// run features | ||||
| 	for _, f := range s.features { | ||||
| 		s.runFeature(f) | ||||
| 		if s.failed && cfg.stopOnFailure { | ||||
|  | @ -136,10 +199,11 @@ func (s *suite) Run() { | |||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	s.fmt.Summary() | ||||
| 	if s.failed { | ||||
| 		os.Exit(1) | ||||
| 	// run after suite handlers | ||||
| 	for _, h := range s.afterSuiteHandlers { | ||||
| 		h.HandleAfterSuite() | ||||
| 	} | ||||
| 	s.fmt.Summary() | ||||
| } | ||||
| 
 | ||||
| func (s *suite) runStep(step *gherkin.Step) (err error) { | ||||
|  | @ -180,18 +244,31 @@ func (s *suite) runStep(step *gherkin.Step) (err error) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (s *suite) runSteps(steps []*gherkin.Step) (st stepsStatus) { | ||||
| func (s *suite) runSteps(steps []*gherkin.Step) (st Status) { | ||||
| 	for _, step := range steps { | ||||
| 		if st != stepsStatusPassed { | ||||
| 		if st == Failed || st == Undefined { | ||||
| 			s.fmt.Skipped(step) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// run before step handlers | ||||
| 		for _, h := range s.beforeStepHandlers { | ||||
| 			h.HandleBeforeStep(step) | ||||
| 		} | ||||
| 
 | ||||
| 		err := s.runStep(step) | ||||
| 		switch { | ||||
| 		case err == errPending: | ||||
| 			st = stepsStatusUndefined | ||||
| 		case err != nil: | ||||
| 			st = stepsStatusFailed | ||||
| 		switch err { | ||||
| 		case errPending: | ||||
| 			st = Undefined | ||||
| 		case nil: | ||||
| 			st = Passed | ||||
| 		default: | ||||
| 			st = Failed | ||||
| 		} | ||||
| 
 | ||||
| 		// run after step handlers | ||||
| 		for _, h := range s.afterStepHandlers { | ||||
| 			h.HandleAfterStep(step, st) | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
|  | @ -206,11 +283,11 @@ func (s *suite) skipSteps(steps []*gherkin.Step) { | |||
| func (s *suite) runFeature(f *gherkin.Feature) { | ||||
| 	s.fmt.Node(f) | ||||
| 	for _, scenario := range f.Scenarios { | ||||
| 		var status stepsStatus | ||||
| 		var status Status | ||||
| 
 | ||||
| 		// run before scenario handlers | ||||
| 		for _, h := range s.beforeScenarioHandlers { | ||||
| 			h.BeforeScenario(scenario) | ||||
| 			h.HandleBeforeScenario(scenario) | ||||
| 		} | ||||
| 
 | ||||
| 		// background | ||||
|  | @ -222,15 +299,20 @@ func (s *suite) runFeature(f *gherkin.Feature) { | |||
| 		// scenario | ||||
| 		s.fmt.Node(scenario) | ||||
| 		switch { | ||||
| 		case status == stepsStatusFailed: | ||||
| 		case status == Failed: | ||||
| 			s.skipSteps(scenario.Steps) | ||||
| 		case status == stepsStatusUndefined: | ||||
| 		case status == Undefined: | ||||
| 			s.skipSteps(scenario.Steps) | ||||
| 		default: | ||||
| 		case status == invalid: | ||||
| 			status = s.runSteps(scenario.Steps) | ||||
| 		} | ||||
| 
 | ||||
| 		if status == stepsStatusFailed { | ||||
| 		// run after scenario handlers | ||||
| 		for _, h := range s.afterScenarioHandlers { | ||||
| 			h.HandleAfterScenario(scenario, status) | ||||
| 		} | ||||
| 
 | ||||
| 		if status == Failed { | ||||
| 			s.failed = true | ||||
| 			if cfg.stopOnFailure { | ||||
| 				return | ||||
|  |  | |||
|  | @ -3,28 +3,35 @@ package godog | |||
| import ( | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/DATA-DOG/godog/gherkin" | ||||
| ) | ||||
| 
 | ||||
| type suiteFeature struct { | ||||
| 	suite | ||||
| 	// for hook tests | ||||
| 	befScenarioHook *gherkin.Scenario | ||||
| type firedEvent struct { | ||||
| 	name string | ||||
| 	args []interface{} | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) BeforeScenario(scenario *gherkin.Scenario) { | ||||
| type suiteFeature struct { | ||||
| 	suite | ||||
| 	events []*firedEvent | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) HandleBeforeScenario(scenario *gherkin.Scenario) { | ||||
| 	// reset feature paths | ||||
| 	cfg.paths = []string{} | ||||
| 	// reset hook test references | ||||
| 	s.befScenarioHook = nil | ||||
| 	// reset event stack | ||||
| 	s.events = []*firedEvent{} | ||||
| 	// reset formatter, which collects all details | ||||
| 	s.fmt = &testFormatter{} | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) iHaveBeforeScenarioHook(args ...*Arg) error { | ||||
| 	s.suite.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) { | ||||
| 		s.befScenarioHook = scenario | ||||
| func (s *suiteFeature) iAmListeningToSuiteEvents(args ...*Arg) error { | ||||
| 	s.BeforeScenario(BeforeScenarioHandlerFunc(func(scenario *gherkin.Scenario) { | ||||
| 		s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{ | ||||
| 			scenario, | ||||
| 		}}) | ||||
| 	})) | ||||
| 	return nil | ||||
| } | ||||
|  | @ -59,10 +66,11 @@ func (s *suiteFeature) iShouldHaveNumFeatureFiles(args ...*Arg) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) iRunFeatures(args ...*Arg) error { | ||||
| 	for _, f := range s.features { | ||||
| 		s.runFeature(f) | ||||
| func (s *suiteFeature) iRunFeatureSuite(args ...*Arg) error { | ||||
| 	if err := s.parseFeatures(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.run() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  | @ -77,14 +85,39 @@ func (s *suiteFeature) numScenariosRegistered(args ...*Arg) (err error) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) iShouldHaveScenarioRecordedInHook(args ...*Arg) (err error) { | ||||
| 	if s.befScenarioHook == nil { | ||||
| 		return fmt.Errorf("there was no scenario executed in before hook") | ||||
| func (s *suiteFeature) thereWereNumEventsFired(args ...*Arg) error { | ||||
| 	var num int | ||||
| 	for _, event := range s.events { | ||||
| 		if event.name == args[2].String() { | ||||
| 			num++ | ||||
| 		} | ||||
| 	} | ||||
| 	if s.befScenarioHook.Title != args[0].String() { | ||||
| 		err = fmt.Errorf(`expected "%s" scenario to be run in hook, but got "%s"`, args[0].String(), s.befScenarioHook.Title) | ||||
| 	if num != args[1].Int() { | ||||
| 		return fmt.Errorf("expected %d %s events to be fired, but got %d", args[1].Int(), args[2].String(), num) | ||||
| 	} | ||||
| 	return | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *suiteFeature) thereWasEventTriggeredBeforeScenario(args ...*Arg) error { | ||||
| 	var found []string | ||||
| 	for _, event := range s.events { | ||||
| 		if event.name != "BeforeScenario" { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		scenario := event.args[0].(*gherkin.Scenario) | ||||
| 		if scenario.Title == args[0].String() { | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		found = append(found, scenario.Title) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(found) == 0 { | ||||
| 		return fmt.Errorf("before scenario event was never triggered or listened") | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, args[0].String(), `"`+strings.Join(found, `", "`)+`"`) | ||||
| } | ||||
| 
 | ||||
| func SuiteContext(g Suite) { | ||||
|  | @ -107,12 +140,15 @@ func SuiteContext(g Suite) { | |||
| 		regexp.MustCompile(`^I should have ([\d]+) scenarios? registered$`), | ||||
| 		StepHandlerFunc(s.numScenariosRegistered)) | ||||
| 	g.Step( | ||||
| 		regexp.MustCompile(`^I have a before scenario hook$`), | ||||
| 		StepHandlerFunc(s.iHaveBeforeScenarioHook)) | ||||
| 		regexp.MustCompile(`^I'm listening to suite events$`), | ||||
| 		StepHandlerFunc(s.iAmListeningToSuiteEvents)) | ||||
| 	g.Step( | ||||
| 		regexp.MustCompile(`^I run features$`), | ||||
| 		StepHandlerFunc(s.iRunFeatures)) | ||||
| 		regexp.MustCompile(`^I run feature suite$`), | ||||
| 		StepHandlerFunc(s.iRunFeatureSuite)) | ||||
| 	g.Step( | ||||
| 		regexp.MustCompile(`^I should have a scenario "([^"]*)" recorded in the hook$`), | ||||
| 		StepHandlerFunc(s.iShouldHaveScenarioRecordedInHook)) | ||||
| 		regexp.MustCompile(`^there (was|were) ([\d]+) "([^"]*)" events? fired$`), | ||||
| 		StepHandlerFunc(s.thereWereNumEventsFired)) | ||||
| 	g.Step( | ||||
| 		regexp.MustCompile(`^there was event triggered before scenario "([^"]*)"$`), | ||||
| 		StepHandlerFunc(s.thereWasEventTriggeredBeforeScenario)) | ||||
| } | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 gedi
						gedi