Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook ordering #43

Merged
merged 3 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions lib/sus/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,31 @@ def inspect
"\#<Sus::Base for #{self.class.description.inspect}>"
end

# A hook which is called before the test is executed.
#
# If you override this method, you must call super.
def before
end

def after
# A hook which is called after the test is executed.
#
# If you override this method, you must call super.
def after(error = nil)
end

def around
# Wrap logic around the test being executed.
#
# Invokes the before hook, then the block, then the after hook.
#
# @yields {...} the block which should execute a test.
def around(&block)
self.before

return yield
return block.call
rescue => error
raise
ensure
self.after
self.after(error)
end

def assert(...)
Expand Down
17 changes: 8 additions & 9 deletions lib/sus/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,36 +78,35 @@ def each(&block)

# Include an around method to the context class, that invokes the given block before running the test.
#
# Before hooks are called in the reverse order they are defined, in other words the last defined before hook is called first.
# Before hooks are usually invoked in the order they are defined, i.e. the first defined hook is invoked first.
#
# @parameter hook [Proc] The block to execute before each test.
def before(&hook)
wrapper = Module.new

wrapper.define_method(:around) do |&block|
wrapper.define_method(:before) do
super()

instance_exec(&hook)
super(&block)
end

self.include(wrapper)
end

# Include an around method to the context class, that invokes the given block after running the test.
#
# After hooks are called in the order they are defined, in other words the last defined after hook is called last.
# After hooks are usually invoked in the reverse order they are defined, i.e. the last defined hook is invoked first.
#
# @parameter hook [Proc] The block to execute after each test. An `error` argument is passed if the test failed with an exception.
def after(&hook)
wrapper = Module.new

wrapper.define_method(:around) do |&block|
error = nil

super(&block)
wrapper.define_method(:after) do |error|
instance_exec(error, &hook)
rescue => error
raise
ensure
instance_exec(error, &hook)
super(error)
end

self.include(wrapper)
Expand Down
2 changes: 1 addition & 1 deletion lib/sus/mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def wrap(method, &hook)
end

module Mocks
def after
def after(error = nil)
super

@mocks&.each_value(&:clear)
Expand Down
Binary file added media/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions test/sus/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,39 @@
it "has a full name" do
expect(instance.full_name).to be == "describe test"
end

with "before hooks" do
let(:events) {Array.new}

before do
events << :before1
end

before do
events << :before2
end

it "invokes before hooks" do
expect(events).to be == [:before1, :before2]
end
end

with "after hooks" do
let(:events) {Array.new}

after do
events << :after1
expect(events).to be == [:example, :after2, :after1]
end

after do
events << :after2
end

it "invokes after hooks" do
events << :example
end
end
end

describe Sus::With do
Expand Down
2 changes: 1 addition & 1 deletion test/sus/have.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def before
@socket = Socket.new(:INET, :STREAM)
end

def after
def after(error = nil)
@socket.close

super
Expand Down
62 changes: 56 additions & 6 deletions test/sus/include_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@

AContextWithHooks = Sus::Shared("a context with hooks") do
before do
events << :shared_before
events << :context_before
end

after do
events << :shared_after
events << :context_after
end

around do |&block|
events << :shared_around_before
super(&block)
events << :context_around_before

super() do
events << :context_around_super_before
block.call
events << :context_around_super_after
end

events << :context_around_after
end
end

Expand All @@ -32,7 +39,7 @@
end
end

with "a shared context with arguments" do
with "a shared context with hooks" do
let(:events) {Array.new}

include AContextWithHooks
Expand All @@ -41,8 +48,51 @@
events << :example_before
end

after do
events << :example_after
end

around do |&block|
events << :example_around_before

super() do
events << :example_around_super_before
block.call
events << :example_around_super_after
end

events << :example_around_after

# This is the full sequence of events:
expect(events).to be == [
:example_around_before,
:context_around_before,
:context_before,
:example_before,
:context_around_super_before,
:example_around_super_before,
:example,
:example_around_super_after,
:context_around_super_after,
:example_after,
:context_after,
:context_around_after,
:example_around_after,
]
end

it "can include a shared context" do
expect(events).to be == [:example_before, :shared_around_before, :shared_before]
events << :example

expect(events).to be == [
:example_around_before,
:context_around_before,
:context_before,
:example_before,
:context_around_super_before,
:example_around_super_before,
:example,
]
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion test/sus/it.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def before
self.before_hook_invoked = true
end

def after
def after(error = nil)
self.after_hook_invoked = true
end

Expand Down
Loading