Skip to content

Commit

Permalink
Merge pull request #3 from nepalez/feature/const
Browse files Browse the repository at this point in the history
Feature/const
  • Loading branch information
nepalez authored Jun 4, 2019
2 parents 504b6ce + a5beaf7 commit f450448
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 54 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [0.0.5] - WIP

### Added

- The support for stubbing constants (nepalez)

```yaml
# Stub constant TIMEOUT_SEC to 10
---
- const: TIMEOUT_SEC
value: 10
```
## [0.0.4] - [2018-05-22]
### Added
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,20 @@ The seed (`seed_fixture`) file should be a YAML/JSON with opinionated parameters
Use the `count: 2` key to create more objects at once.

Another opinionated format we use for stubs (`stub_fixture`):
Another opinionated format we use for stubs (`stub_fixture`). The gem supports stubbing both message chains and constants.

For message chains:

- `class` for stubbed class
- `chain` for messages chain
- `arguments` (optional) for specific arguments
- `actions` for an array of actions for consecutive invocations of the chain

For constants:

- `const` for stubbed constant
- `value` for a value of the constant

Every action either `return` some value, or `raise` some exception

```yaml
Expand Down Expand Up @@ -128,6 +135,9 @@ Every action either `return` some value, or `raise` some exception
actions:
- return: true
- raise: ActiveRecord::RecordNotFound
- const: NOTIFIER_TIMEOUT_SEC
value: 10
```

```graphql
Expand Down
56 changes: 33 additions & 23 deletions lib/fixturama/stubs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,57 @@ module Fixturama
# Collection of stubbed calls
#
class Stubs
require_relative "stubs/actions"
require_relative "stubs/arguments"
require_relative "stubs/chain"
require_relative "stubs/const"

#
# Register new action and apply the corresponding stub
#
# @option [#to_s] :class Class to stub
# @option [Array<#to_s>] :chain Methods chain for stubbing
# @option (see Fixturama::Stubs::Method#add)
# @params [Hash<#to_s, _>] options
# @return [self] itself
#
def add(options)
tap do
options = Utils.symbolize_hash(options)

options.select { |key| %i[class chain].include?(key) }.tap do |anchors|
chains[anchors] ||= Chain.new(anchors)
chains[anchors].add(options)
end
end
options = symbolize(options)
find_or_create_stub!(options)&.update!(options)
self
end

#
# Applies the stub to RSpec example
#
def apply(example)
chains.values.each do |chain|
chain.reset!
call_action = \
example.send(:receive_message_chain, *chain.messages) do |*args|
chain.call! args
end
example.send(:allow, chain.receiver).to call_action
end
@stubs.values.each { |stub| stub.apply!(example) }
end

private

def chains
@chains ||= {}
def initialize
@stubs = {}
end

def find_or_create_stub!(options)
case stub_type(options)
when :message_chain
anchor = options.slice(:class, :chain)
@stubs[anchor] ||= Chain.new(anchor)
when :constant
anchor = options.slice(:const)
@stubs[anchor] ||= Const.new(anchor)
end
end

def stub_type(options)
return :message_chain if options[:class]
return :constant if options[:const]

raise ArgumentError, <<~MESSAGE
Cannot figure out what to stub from #{options}.
You should define either a class and a message chain, or some const.
MESSAGE
end

def symbolize(options)
Hash(options).transform_keys { |key| key.to_s.to_sym }
end
end
end
56 changes: 32 additions & 24 deletions lib/fixturama/stubs/chain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,32 @@ module Fixturama
# Stubbed chain of messages
#
class Stubs::Chain
require_relative "chain/actions"
require_relative "chain/arguments"

attr_reader :receiver, :messages

#
# Human-readable representation of the chain
# @return [String]
#
def to_s
"#{receiver}.#{messages.join(".")}"
end
alias to_str to_s

#
# Register new action for some arguments
#
# @option [Array<#to_s>, #to_s] :arguments The specific arguments
# @option (see Fixturama::Stubs::Arguments#add_action)
# @return [self]
#
def add(actions:, arguments: nil, **)
def update!(actions:, arguments: nil, **)
Utils.array(arguments).tap do |args|
stub = find_by(args)
unless stub
stub = Stubs::Arguments.new(self, args)
stub = Stubs::Chain::Arguments.new(self, args)
stubs << stub
end
stub.add!(*actions)
Expand All @@ -27,31 +39,16 @@ def add(actions:, arguments: nil, **)
end

#
# Resets all counters
# @return [self] itself
# Apply the stub to RSpec example
#
def reset!
tap { stubs.each(&:reset!) }
end
def apply!(example)
reset!

#
# Executes the corresponding action
# @return [Object]
# @raise [StandardError]
#
def call!(actual_arguments)
stub = stubs.find { |item| item.applicable_to?(actual_arguments) }
raise "Unexpected arguments #{actual_arguments}" unless stub

stub.call_next!
end
call_action = example.send(:receive_message_chain, *messages) do |*args|
call! args
end

#
# Human-readable representation of the chain
# @return [String]
#
def to_s
"#{receiver}.#{messages.join(".")}"
example.send(:allow, receiver).to call_action
end

private
Expand All @@ -74,5 +71,16 @@ def stubs
def find_by(arguments)
stubs.find { |stub| stub.arguments == arguments }
end

def reset!
tap { stubs.each(&:reset!) }
end

def call!(actual_arguments)
stub = stubs.find { |item| item.applicable_to?(actual_arguments) }
raise "Unexpected arguments #{actual_arguments}" unless stub

stub.call_next!
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Fixturama
#
# Factory to provide a specific action from options
#
module Stubs::Actions
module Stubs::Chain::Actions
extend self

require_relative "actions/raise"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Fixturama::Stubs::Actions::Raise
class Fixturama::Stubs::Chain::Actions::Raise
def call
raise @exception
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class Fixturama::Stubs::Actions::Return
class Fixturama::Stubs::Chain::Actions::Return
attr_reader :stub, :call

#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Fixturama
#
# Collection of arguments for a stub with a list of actions to be called
#
class Stubs::Arguments
class Stubs::Chain::Arguments
attr_reader :chain, :arguments

#
Expand All @@ -15,7 +15,7 @@ def add!(*actions)
actions.each do |settings|
settings = Utils.symbolize_hash(settings)
repeat = [0, settings.fetch(:repeat, 1).to_i].max
repeat.times { list << Stubs::Actions.build(self, settings) }
repeat.times { list << Stubs::Chain::Actions.build(self, settings) }
end

self
Expand Down
42 changes: 42 additions & 0 deletions lib/fixturama/stubs/const.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Fixturama
#
# Definition for stubbing a constant
#
class Stubs::Const
attr_reader :const, :value

#
# Human-readable representation of the chain
# @return [String]
#
def to_s
const.to_s
end
alias to_str to_s

#
# Overload the definition for the constant
# @option [Object] value
# @return [self]
#
def update!(value:, **)
@value = value
self
end

#
# Apply the stub to RSpec example
# @return [self]
#
def apply!(example)
example.send(:stub_const, const, value)
self
end

private

def initialize(const:, **)
@const = const.to_s
end
end
end
13 changes: 12 additions & 1 deletion spec/fixturama/stub_fixture/_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def pay(_)
it { is_expected.to eq [5] }
end

context "with stubbing" do
context "when message chain stubbed" do
before { stub_fixture "#{__dir__}/stub.yml" }

context "with a :raise option" do
Expand Down Expand Up @@ -66,4 +66,15 @@ def pay(_)
end
end
end

context "when constant stubbed" do
before do
TIMEOUT = 20
stub_fixture "#{__dir__}/stub.yml"
end

it "stubs the constant" do
expect(TIMEOUT).to eq 10
end
end
end
3 changes: 3 additions & 0 deletions spec/fixturama/stub_fixture/stub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@
- return: 6
repeat: 2
- return: 0

- const: TIMEOUT
value: 10

0 comments on commit f450448

Please sign in to comment.