Support defining hooks in configuration block

This commit is contained in:
Michael Miller 2021-07-17 15:20:58 -06:00
parent 009266c8c2
commit 937b084f66
No known key found for this signature in database
GPG key ID: FB9F12F7C646A4AD
6 changed files with 164 additions and 1 deletions

View file

@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Examples can be skipped during execution by using `skip` or `pending` in the example block.
- Sample blocks can be temporarily skipped by using `xsample` or `xrandom_sample`.
- Add `before_suite` and `after_suite` hooks.
- Support defining hooks in `Spectator.configure` block.
### Changed
- Simplify and reduce defined types and generics. Should speed up compilation times.

View file

@ -20,6 +20,27 @@ module Spectator
# Filter used to select which examples to run.
getter example_filter : ExampleFilter
# List of hooks to run before all examples in the test suite.
protected getter before_suite_hooks : Array(ExampleGroupHook)
# List of hooks to run before each top-level example group.
protected getter before_all_hooks : Array(ExampleGroupHook)
# List of hooks to run before every example.
protected getter before_each_hooks : Array(ExampleHook)
# List of hooks to run after all examples in the test suite.
protected getter after_suite_hooks : Array(ExampleGroupHook)
# List of hooks to run after each top-level example group.
protected getter after_all_hooks : Array(ExampleGroupHook)
# List of hooks to run after every example.
protected getter after_each_hooks : Array(ExampleHook)
# List of hooks to run around every example.
protected getter around_each_hooks : Array(ExampleProcsyHook)
# Creates a new configuration.
# Properties are pulled from *source*.
# Typically, *source* is a `Config::Builder`.
@ -28,6 +49,14 @@ module Spectator
@run_flags = source.run_flags
@random_seed = source.random_seed
@example_filter = source.example_filter
@before_suite_hooks = source.before_suite_hooks
@before_all_hooks = source.before_all_hooks
@before_each_hooks = source.before_each_hooks
@after_suite_hooks = source.after_suite_hooks
@after_all_hooks = source.after_all_hooks
@after_each_hooks = source.after_each_hooks
@around_each_hooks = source.around_each_hooks
end
# Produces the default configuration.

View file

@ -20,6 +20,103 @@ module Spectator
@additional_formatters = [] of Formatting::Formatter
@filters = [] of ExampleFilter
# List of hooks to run before all examples in the test suite.
protected getter before_suite_hooks = [] of ExampleGroupHook
# List of hooks to run before each top-level example group.
protected getter before_all_hooks = [] of ExampleGroupHook
# List of hooks to run before every example.
protected getter before_each_hooks = [] of ExampleHook
# List of hooks to run after all examples in the test suite.
protected getter after_suite_hooks = [] of ExampleGroupHook
# List of hooks to run after each top-level example group.
protected getter after_all_hooks = [] of ExampleGroupHook
# List of hooks to run after every example.
protected getter after_each_hooks = [] of ExampleHook
# List of hooks to run around every example.
protected getter around_each_hooks = [] of ExampleProcsyHook
# Attaches a hook to be invoked before all examples in the test suite.
def add_before_suite_hook(hook)
@before_suite_hooks << hook
end
# Defines a block of code to execute before all examples in the test suite.
def before_suite(&block)
@before_suite_hooks << ExampleGroupHook.new(&block)
end
# Attaches a hook to be invoked before each top-level example group.
def add_before_all_hook(hook)
@before_all_hooks << hook
end
# Defines a block of code to execute before each top-level example group.
def before_all(&block)
@before_all_hooks << ExampleGroupHook.new(&block)
end
# Attaches a hook to be invoked before every example.
# The current example is provided as a block argument.
def add_before_each_hook(hook)
@before_each_hooks << hook
end
# Defines a block of code to execute before every.
# The current example is provided as a block argument.
def before_each(&block : Example -> _)
@before_each_hooks << ExampleHook.new(&block)
end
# Attaches a hook to be invoked after all examples in the test suite.
def add_after_suite_hook(hook)
@after_suite_hooks << hook
end
# Defines a block of code to execute after all examples in the test suite.
def after_suite(&block)
@after_suite_hooks << ExampleGroupHook.new(&block)
end
# Attaches a hook to be invoked after each top-level example group.
def add_after_all_hook(hook)
@after_all_hooks << hook
end
# Defines a block of code to execute after each top-level example group.
def after_all(&block)
@after_all_hooks << ExampleGroupHook.new(&block)
end
# Attaches a hook to be invoked after every example.
# The current example is provided as a block argument.
def add_after_each_hook(hook)
@after_each_hooks << hook
end
# Defines a block of code to execute after every example.
# The current example is provided as a block argument.
def after_each(&block : Example -> _)
@after_each_hooks << ExampleHook.new(&block)
end
# Attaches a hook to be invoked around every example.
# The current example in procsy form is provided as a block argument.
def add_around_each_hook(hook)
@around_each_hooks << hook
end
# Defines a block of code to execute around every example.
# The current example in procsy form is provided as a block argument.
def around_each(&block : Example -> _)
@around_each_hooks << ExampleProcsyHook.new(label: "around_each", &block)
end
# Creates a configuration.
def build : Config
Config.new(self)

View file

@ -33,6 +33,12 @@ module Spectator
@%hooks << hook
end
# Adds a hook to be invoked when the *{{name.id}}* event occurs.
# The hook is added to the front of the list.
def prepend_{{name.id}}_hook(hook : ExampleGroupHook) : Nil
@%hooks.unshift(hook)
end
# Defines a hook for the *{{name.id}}* event.
# The block of code given to this method is invoked when the event occurs.
def {{name.id}}(&block : -> _) : Nil
@ -86,6 +92,12 @@ module Spectator
@%hooks << hook
end
# Adds a hook to be invoked when the *{{name.id}}* event occurs.
# The hook is added to the front of the list.
def prepend_{{name.id}}_hook(hook : ExampleHook) : Nil
@%hooks.unshift(hook)
end
# Defines a hook for the *{{name.id}}* event.
# The block of code given to this method is invoked when the event occurs.
# The current example is provided as a block argument.

View file

@ -166,6 +166,12 @@ module Spectator
@around_hooks << hook
end
# Adds a hook to be invoked when the *around_each* event occurs.
# The hook is added to the front of the list.
def prepend_around_each_hook(hook : ExampleProcsyHook) : Nil
@around_hooks.unshift(hook)
end
# Defines a hook for the *around_each* event.
# The block of code given to this method is invoked when the event occurs.
# The current example is provided as a block argument.

View file

@ -43,7 +43,25 @@ module Spectator
def build : Spec
raise "Mismatched start and end groups" unless root?
Spec.new(root.build, config)
group = root.build
# Apply hooks from configuration.
config.before_suite_hooks.each { |hook| group.prepend_before_all_hook(hook) }
config.after_suite_hooks.each { |hook| group.prepend_after_all_hook(hook) }
config.before_each_hooks.each { |hook| group.prepend_before_each_hook(hook) }
config.after_each_hooks.each { |hook| group.prepend_after_each_hook(hook) }
config.around_each_hooks.each { |hook| group.prepend_around_each_hook(hook) }
# `before_all` and `after_all` hooks are slightly different.
# They are applied to every top-level group (groups just under root).
group.each do |node|
next unless node.is_a?(Events)
config.before_all_hooks.reverse_each { |hook| node.prepend_before_all_hook(hook) }
config.after_all_hooks.reverse_each { |hook| node.prepend_after_all_hook(hook) }
end
Spec.new(group, config)
end
# Defines a new example group and pushes it onto the group stack.