Module: RSpec::SleepingKingStudios::Concerns::IncludeContract
- Defined in:
- lib/rspec/sleeping_king_studios/concerns/include_contract.rb
Overview
Defines helpers for including reusable contracts in RSpec example groups.
RSpec contracts are a mechanism for sharing tests between projects. For example, one library may define an interface or specification for a type of object, while a second library implements that object. By defining a contract and sharing that contract as part of the library, the developer ensures that any object that matches the contract has correctly implemented and conforms to the interface. This reduces duplication of tests and provides resiliency as an interface is developed over time and across versions of the library.
Mechanically speaking, each contract encapsulates a section of RSpec code. When the contract is included in a spec, that code is then injected into the spec. Writing a contract, therefore, is no different than writing any other RSpec specification - it is only the delivery mechanism that differs. A contract can be any object that responds to #to_proc; the simplest contract is therefore a Proc or lambda that contains some RSpec code.
Class Method Summary collapse
- .define_contract_method(context:, contract:, name:) ⇒ Object
- .resolve_contract(context:, contract_or_name:) ⇒ Object
Instance Method Summary collapse
-
#finclude_contract(contract_or_name, *arguments, **keywords, &block) ⇒ Object
As #include_contract, but wraps the contract in a focused example group.
-
#include_contract(contract_or_name, *arguments, **keywords, &block) ⇒ Object
Adds the contract to the example group with the given parameters.
-
#xinclude_contract(contract_or_name, *arguments, **keywords, &block) ⇒ Object
As #include_contract, but wraps the contract in a skipped example group.
Class Method Details
.define_contract_method(context:, contract:, name:) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rspec/sleeping_king_studios/concerns/include_contract.rb', line 114 def define_contract_method(context:, contract:, name:) method_name = +'rspec_include_contract' method_name << '_' << tools.str.underscore(name) if contract_name?(name) method_name << '_' << tools.str.underscore(SecureRandom.uuid) method_name = method_name.tr(' ', '_').intern context.define_singleton_method(method_name, &contract) yield method_name ensure if context.singleton_class.respond_to?(method_name) context.singleton_class.remove_method(method_name) end end |
.resolve_contract(context:, contract_or_name:) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/rspec/sleeping_king_studios/concerns/include_contract.rb', line 130 def resolve_contract(context:, contract_or_name:) validate_contract!(contract_or_name) return contract_or_name unless contract_name?(contract_or_name) contract_name = contract_or_name.to_s contract = resolve_contract_class(context, "#{contract_name} contract") || resolve_contract_const(context, "#{contract_name} contract") || resolve_contract_class(context, contract_name) || resolve_contract_const(context, contract_name) return contract if contract raise ArgumentError, "undefined contract #{contract_or_name.inspect}" end |
Instance Method Details
#finclude_contract(contract_or_name, *arguments, **keywords, &block) ⇒ Object
As #include_contract, but wraps the contract in a focused example group.
195 196 197 198 199 200 201 202 203 |
# File 'lib/rspec/sleeping_king_studios/concerns/include_contract.rb', line 195 def finclude_contract(contract_or_name, *arguments, **keywords, &block) fdescribe '(focused)' do if keywords.empty? include_contract(contract_or_name, *arguments, &block) else include_contract(contract_or_name, *arguments, **keywords, &block) end end end |
#include_contract(contract, *arguments, **keywords) { ... } ⇒ Object #include_contract(contract_name, *arguments, **keywords) { ... } ⇒ Object
Adds the contract to the example group with the given parameters.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/rspec/sleeping_king_studios/concerns/include_contract.rb', line 227 def include_contract(contract_or_name, *arguments, **keywords, &block) # rubocop:disable Metrics/MethodLength concern = RSpec::SleepingKingStudios::Concerns::IncludeContract contract = concern.resolve_contract( context: self, contract_or_name: contract_or_name ) concern.define_contract_method( context: self, contract: contract, name: contract_or_name ) do |method_name| if keywords.empty? send(method_name, *arguments, &block) else send(method_name, *arguments, **keywords, &block) end end end |
#xinclude_contract(contract_or_name, *arguments, **keywords, &block) ⇒ Object
As #include_contract, but wraps the contract in a skipped example group.
250 251 252 253 254 255 256 257 258 |
# File 'lib/rspec/sleeping_king_studios/concerns/include_contract.rb', line 250 def xinclude_contract(contract_or_name, *arguments, **keywords, &block) xdescribe '(skipped)' do if keywords.empty? include_contract(contract_or_name, *arguments, &block) else include_contract(contract_or_name, *arguments, **keywords, &block) end end end |