From f47c48b483c8114cb0e50e350fb4434e6b01a601 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Thu, 26 Sep 2019 21:37:29 -0600 Subject: [PATCH] Add pre- and post-conditions --- src/spectator/dsl/hooks.cr | 30 +++++++++++++++++++ src/spectator/example_conditions.cr | 20 ++++++++----- src/spectator/runnable_example.cr | 5 +++- src/spectator/spec_builder.cr | 4 +-- .../spec_builder/example_group_builder.cr | 17 +++++++++++ .../nested_example_group_builder.cr | 2 +- .../root_example_group_builder.cr | 2 +- .../sample_example_group_builder.cr | 4 +-- src/spectator/test_context.cr | 12 +++++++- 9 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/spectator/dsl/hooks.cr b/src/spectator/dsl/hooks.cr index 00bdd14..5254068 100644 --- a/src/spectator/dsl/hooks.cr +++ b/src/spectator/dsl/hooks.cr @@ -45,5 +45,35 @@ module Spectator ::Spectator::SpecBuilder.add_around_each_hook { |test, proc| test.as({{@type.id}}).%hook(proc) } end + + macro pre_condition(&block) + def %hook({{block.args.splat}}) : Nil + {{block.body}} + end + + ::Spectator::SpecBuilder.add_pre_condition do |test, example| + cast_test = test.as({{@type.id}}) + {% if block.args.empty? %} + cast_test.%hook + {% else %} + cast_test.%hook(example) + {% end %} + end + end + + macro post_condition(&block) + def %hook({{block.args.splat}}) : Nil + {{block.body}} + end + + ::Spectator::SpecBuilder.add_post_condition do |test, example| + cast_test = test.as({{@type.id}}) + {% if block.args.empty? %} + cast_test.%hook + {% else %} + cast_test.%hook(example) + {% end %} + end + end end end diff --git a/src/spectator/example_conditions.cr b/src/spectator/example_conditions.cr index d1fc95a..0fbd03d 100644 --- a/src/spectator/example_conditions.cr +++ b/src/spectator/example_conditions.cr @@ -10,28 +10,32 @@ module Spectator # This will effectively run nothing extra while running a test. def self.empty new( - [] of ->, - [] of -> + [] of TestMetaMethod, + [] of TestMetaMethod ) end # Creates a new set of conditions. def initialize( - @pre_conditions : Array(->), - @post_conditions : Array(->) + @pre_conditions : Array(TestMetaMethod), + @post_conditions : Array(TestMetaMethod) ) end # Runs all pre-condition checks. # These should be run before every test. - def run_pre_conditions - @pre_conditions.each &.call + def run_pre_conditions(wrapper : TestWrapper, example : Example) + @pre_conditions.each do |hook| + wrapper.call(hook, example) + end end # Runs all post-condition checks. # These should be run after every test. - def run_post_conditions - @post_conditions.each &.call + def run_post_conditions(wrapper : TestWrapper, example : Example) + @post_conditions.each do |hook| + wrapper.call(hook, example) + end end end end diff --git a/src/spectator/runnable_example.cr b/src/spectator/runnable_example.cr index c608619..d981689 100644 --- a/src/spectator/runnable_example.cr +++ b/src/spectator/runnable_example.cr @@ -25,12 +25,15 @@ module Spectator # Runs the test code and captures the result. private def run_example(result) - wrapper = test_wrapper.around_hook(group.context) + context = group.context + wrapper = test_wrapper.around_hook(context) # Capture how long it takes to run the test code. result.elapsed = Time.measure do begin + context.run_pre_conditions(self) wrapper.call + context.run_post_conditions(self) rescue ex # Catch all errors and handle them later. result.error = ex end diff --git a/src/spectator/spec_builder.cr b/src/spectator/spec_builder.cr index ed897da..e8ec95e 100644 --- a/src/spectator/spec_builder.cr +++ b/src/spectator/spec_builder.cr @@ -87,12 +87,12 @@ module Spectator end # Adds a pre-condition to run at the start of every example in the current group. - def add_pre_condition(&block : ->) : Nil + def add_pre_condition(&block : TestMetaMethod) : Nil @@stack.current.add_pre_condition(block) end # Adds a post-condition to run at the end of every example in the current group. - def add_post_condition(&block : ->) : Nil + def add_post_condition(&block : TestMetaMethod) : Nil @@stack.current.add_post_condition(block) end diff --git a/src/spectator/spec_builder/example_group_builder.cr b/src/spectator/spec_builder/example_group_builder.cr index 344ea04..f9ed5bf 100644 --- a/src/spectator/spec_builder/example_group_builder.cr +++ b/src/spectator/spec_builder/example_group_builder.cr @@ -12,6 +12,8 @@ module Spectator::SpecBuilder @before_all_hooks = Deque(->).new @after_all_hooks = Deque(->).new @around_each_hooks = Deque(::SpectatorTest, Proc(Nil) ->).new + @pre_conditions = Deque(TestMetaMethod).new + @post_conditions = Deque(TestMetaMethod).new def add_child(child : Child) @children << child @@ -37,6 +39,14 @@ module Spectator::SpecBuilder @around_each_hooks << hook end + def add_pre_condition(hook : TestMetaMethod) + @pre_conditions << hook + end + + def add_post_condition(hook : TestMetaMethod) + @post_conditions << hook + end + private def build_hooks ExampleHooks.new( @before_all_hooks.to_a, @@ -46,5 +56,12 @@ module Spectator::SpecBuilder @around_each_hooks.to_a ) end + + private def build_conditions + ExampleConditions.new( + @pre_conditions.to_a, + @post_conditions.to_a + ) + end end end diff --git a/src/spectator/spec_builder/nested_example_group_builder.cr b/src/spectator/spec_builder/nested_example_group_builder.cr index efe62e5..dc08cff 100644 --- a/src/spectator/spec_builder/nested_example_group_builder.cr +++ b/src/spectator/spec_builder/nested_example_group_builder.cr @@ -7,7 +7,7 @@ module Spectator::SpecBuilder end def build(parent_group) - context = TestContext.new(parent_group.context, build_hooks, parent_group.context.values) + context = TestContext.new(parent_group.context, build_hooks, build_conditions, parent_group.context.values) NestedExampleGroup.new(@description, @source, parent_group, context).tap do |group| group.children = children.map do |child| child.build(group).as(ExampleComponent) diff --git a/src/spectator/spec_builder/root_example_group_builder.cr b/src/spectator/spec_builder/root_example_group_builder.cr index 09cb104..02f4914 100644 --- a/src/spectator/spec_builder/root_example_group_builder.cr +++ b/src/spectator/spec_builder/root_example_group_builder.cr @@ -4,7 +4,7 @@ require "./example_group_builder" module Spectator::SpecBuilder class RootExampleGroupBuilder < ExampleGroupBuilder def build - context = TestContext.new(nil, build_hooks, TestValues.empty) + context = TestContext.new(nil, build_hooks, build_conditions, TestValues.empty) RootExampleGroup.new(context).tap do |group| group.children = children.map do |child| child.build(group).as(ExampleComponent) diff --git a/src/spectator/spec_builder/sample_example_group_builder.cr b/src/spectator/spec_builder/sample_example_group_builder.cr index c8013e3..eacd596 100644 --- a/src/spectator/spec_builder/sample_example_group_builder.cr +++ b/src/spectator/spec_builder/sample_example_group_builder.cr @@ -9,7 +9,7 @@ module Spectator::SpecBuilder def build(parent_group) values = parent_group.context.values collection = @collection_builder.call(values) - context = TestContext.new(parent_group.context, build_hooks, values) + context = TestContext.new(parent_group.context, build_hooks, build_conditions, values) NestedExampleGroup.new(@description, @source, parent_group, context).tap do |group| group.children = collection.map do |element| build_sub_group(group, element).as(ExampleComponent) @@ -19,7 +19,7 @@ module Spectator::SpecBuilder private def build_sub_group(parent_group, element) values = parent_group.context.values.add(@id, @description.to_s, element) - context = TestContext.new(parent_group.context, ExampleHooks.empty, values) + context = TestContext.new(parent_group.context, ExampleHooks.empty, ExampleConditions.empty, values) NestedExampleGroup.new("#{@label} = #{element.inspect}", @source, parent_group, context).tap do |group| group.children = children.map do |child| child.build(group).as(ExampleComponent) diff --git a/src/spectator/test_context.cr b/src/spectator/test_context.cr index 0bc421d..fa7a720 100644 --- a/src/spectator/test_context.cr +++ b/src/spectator/test_context.cr @@ -5,7 +5,7 @@ module Spectator class TestContext getter values - def initialize(@parent : TestContext?, @hooks : ExampleHooks, @values : TestValues) + def initialize(@parent : TestContext?, @hooks : ExampleHooks, @conditions : ExampleConditions, @values : TestValues) @before_all_hooks_run = false @after_all_hooks_run = false end @@ -56,5 +56,15 @@ module Spectator wrapper end end + + def run_pre_conditions(example) + @parent.try &.run_pre_conditions(example) + @conditions.run_pre_conditions(example.test_wrapper, example) + end + + def run_post_conditions(example) + @conditions.run_post_conditions(example.test_wrapper, example) + @parent.try &.run_post_conditions(example) + end end end