mocktail 0.0.2 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/Gemfile.lock +2 -2
- data/README.md +178 -18
- data/bin/console +21 -1
- data/lib/mocktail/explains_thing.rb +132 -0
- data/lib/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +13 -0
- data/lib/mocktail/handles_dry_call/fulfills_stubbing.rb +4 -0
- data/lib/mocktail/handles_dry_call/validates_arguments.rb +2 -23
- data/lib/mocktail/imitates_type/makes_double/declares_dry_class.rb +29 -23
- data/lib/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +21 -0
- data/lib/mocktail/imitates_type/makes_double.rb +8 -4
- data/lib/mocktail/raises_neato_no_method_error.rb +81 -0
- data/lib/mocktail/share/creates_identifier.rb +28 -0
- data/lib/mocktail/{verifies_call/raises_verification_error → share}/stringifies_call.rb +16 -7
- data/lib/mocktail/share/stringifies_method_name.rb +11 -0
- data/lib/mocktail/simulates_argument_error/cleans_backtrace.rb +17 -0
- data/lib/mocktail/simulates_argument_error/reconciles_args_with_params.rb +20 -0
- data/lib/mocktail/simulates_argument_error/recreates_message.rb +29 -0
- data/lib/mocktail/simulates_argument_error/transforms_params.rb +32 -0
- data/lib/mocktail/simulates_argument_error.rb +30 -0
- data/lib/mocktail/value/cabinet.rb +12 -0
- data/lib/mocktail/value/double.rb +7 -8
- data/lib/mocktail/value/double_data.rb +10 -0
- data/lib/mocktail/value/explanation.rb +26 -0
- data/lib/mocktail/value/signature.rb +36 -0
- data/lib/mocktail/value/stub_returned_nil.rb +26 -0
- data/lib/mocktail/value/top_shelf.rb +24 -25
- data/lib/mocktail/value/type_replacement_data.rb +13 -0
- data/lib/mocktail/value/unsatisfied_stubbing.rb +8 -0
- data/lib/mocktail/value.rb +6 -0
- data/lib/mocktail/verifies_call/raises_verification_error.rb +4 -2
- data/lib/mocktail/version.rb +1 -1
- data/lib/mocktail.rb +9 -0
- data/mocktail.gemspec +1 -1
- metadata +22 -6
- data/lib/mocktail/share/simulates_argument_error.rb +0 -28
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative "share/stringifies_call"
|
2
|
+
require_relative "share/stringifies_method_name"
|
3
|
+
require_relative "share/creates_identifier"
|
4
|
+
|
5
|
+
module Mocktail
|
6
|
+
class RaisesNeatoNoMethodError
|
7
|
+
def initialize
|
8
|
+
@stringifies_call = StringifiesCall.new
|
9
|
+
@stringifies_method_name = StringifiesMethodName.new
|
10
|
+
@creates_identifier = CreatesIdentifier.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(call)
|
14
|
+
raise NoMethodError.new <<~MSG
|
15
|
+
No method `#{@stringifies_method_name.stringify(call)}' exists for call:
|
16
|
+
|
17
|
+
#{@stringifies_call.stringify(call, anonymous_blocks: true, always_parens: true)}
|
18
|
+
|
19
|
+
Need to define the method? Here's a sample definition:
|
20
|
+
|
21
|
+
def #{call.method}#{params(call)}
|
22
|
+
end
|
23
|
+
#{corrections(call)}
|
24
|
+
MSG
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def params(call)
|
30
|
+
return if (params_lists = [
|
31
|
+
params_list(call.args),
|
32
|
+
kwparams_list(call.kwargs),
|
33
|
+
block_param(call.block)
|
34
|
+
].compact).empty?
|
35
|
+
|
36
|
+
"(#{params_lists.join(", ")})"
|
37
|
+
end
|
38
|
+
|
39
|
+
def params_list(args)
|
40
|
+
return if args.empty?
|
41
|
+
|
42
|
+
count_repeats(args.map { |arg|
|
43
|
+
@creates_identifier.create(arg, default: "arg")
|
44
|
+
}).join(", ")
|
45
|
+
end
|
46
|
+
|
47
|
+
def kwparams_list(kwargs)
|
48
|
+
return if kwargs.empty?
|
49
|
+
|
50
|
+
kwargs.keys.map { |key| "#{key}:" }.join(", ")
|
51
|
+
end
|
52
|
+
|
53
|
+
def block_param(block)
|
54
|
+
return if block.nil?
|
55
|
+
|
56
|
+
"&blk"
|
57
|
+
end
|
58
|
+
|
59
|
+
def count_repeats(identifiers)
|
60
|
+
identifiers.map.with_index { |id, i|
|
61
|
+
if (preceding_matches = identifiers[0...i].count { |other_id| id == other_id }) > 0
|
62
|
+
"#{id}#{preceding_matches + 1}"
|
63
|
+
else
|
64
|
+
id
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def corrections(call)
|
70
|
+
return if (corrections = DidYouMean::SpellChecker.new(dictionary: call.original_type.instance_methods).correct(call.method)).empty?
|
71
|
+
|
72
|
+
<<~MSG
|
73
|
+
|
74
|
+
There #{corrections.size == 1 ? "is" : "are"} also #{corrections.size} similar method#{"s" if corrections.size != 1} on #{call.original_type.name}.
|
75
|
+
|
76
|
+
Did you mean?
|
77
|
+
#{corrections.map { |c| " #{c}" }.join("\n")}
|
78
|
+
MSG
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class CreatesIdentifier
|
3
|
+
KEYWORDS = %w[__FILE__ __LINE__ alias and begin BEGIN break case class def defined? do else elsif end END ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield]
|
4
|
+
|
5
|
+
def create(s, default: "identifier", max_length: 24)
|
6
|
+
id = s.to_s.downcase
|
7
|
+
.gsub(/:0x[0-9a-f]+/, "") # Lazy attempt to wipe any Object:0x802beef identifiers
|
8
|
+
.gsub(/[^\w\s]/, "")
|
9
|
+
.gsub(/^\d+/, "")[0...max_length]
|
10
|
+
.strip
|
11
|
+
.gsub(/\s+/, "_") # snake_case
|
12
|
+
|
13
|
+
if id.empty?
|
14
|
+
default
|
15
|
+
else
|
16
|
+
unreserved(id, default)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def unreserved(id, default)
|
23
|
+
return id unless KEYWORDS.include?(id)
|
24
|
+
|
25
|
+
"#{id}_#{default}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,18 +1,22 @@
|
|
1
1
|
module Mocktail
|
2
2
|
class StringifiesCall
|
3
|
-
def stringify(call)
|
4
|
-
"#{call.method}#{args_to_s(call)}#{blockify(call.block)}"
|
3
|
+
def stringify(call, anonymous_blocks: false, always_parens: false)
|
4
|
+
"#{call.method}#{args_to_s(call, parens: always_parens)}#{blockify(call.block, anonymous: anonymous_blocks)}"
|
5
5
|
end
|
6
6
|
|
7
7
|
private
|
8
8
|
|
9
|
-
def args_to_s(call)
|
10
|
-
|
9
|
+
def args_to_s(call, parens: true)
|
10
|
+
args_lists = [
|
11
11
|
argify(call.args),
|
12
12
|
kwargify(call.kwargs),
|
13
13
|
lambdafy(call.block)
|
14
|
-
].compact
|
14
|
+
].compact
|
15
|
+
|
16
|
+
if !args_lists.empty?
|
15
17
|
"(#{args_lists.join(", ")})"
|
18
|
+
elsif parens
|
19
|
+
"()"
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
@@ -31,9 +35,14 @@ module Mocktail
|
|
31
35
|
"&lambda[#{source_locationify(block)}]"
|
32
36
|
end
|
33
37
|
|
34
|
-
def blockify(block)
|
38
|
+
def blockify(block, anonymous:)
|
35
39
|
return unless block && !block.lambda?
|
36
|
-
|
40
|
+
|
41
|
+
if anonymous
|
42
|
+
" {…}"
|
43
|
+
else
|
44
|
+
" { Proc at #{source_locationify(block)} }"
|
45
|
+
end
|
37
46
|
end
|
38
47
|
|
39
48
|
def source_locationify(block)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Mocktail
|
4
|
+
class CleansBacktrace
|
5
|
+
BASE_PATH = (Pathname.new(__FILE__) + "../../..").to_s
|
6
|
+
|
7
|
+
def clean(error)
|
8
|
+
raise error
|
9
|
+
rescue => e
|
10
|
+
e.tap do |e|
|
11
|
+
e.set_backtrace(e.backtrace.drop_while { |frame|
|
12
|
+
frame.start_with?(BASE_PATH)
|
13
|
+
})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class ReconcilesArgsWithParams
|
3
|
+
def reconcile(signature)
|
4
|
+
args_match?(signature.positional_params, signature.positional_args) &&
|
5
|
+
kwargs_match?(signature.keyword_params, signature.keyword_args)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def args_match?(arg_params, args)
|
11
|
+
args.size >= arg_params.required.size &&
|
12
|
+
(arg_params.rest? || args.size <= arg_params.allowed.size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def kwargs_match?(kwarg_params, kwargs)
|
16
|
+
kwarg_params.required.all? { |name| kwargs.key?(name) } &&
|
17
|
+
(kwarg_params.rest? || kwargs.keys.all? { |name| kwarg_params.allowed.include?(name) })
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class RecreatesMessage
|
3
|
+
def recreate(signature)
|
4
|
+
req_args = signature.positional_params.required.size
|
5
|
+
allowed_args = signature.positional_params.allowed.size
|
6
|
+
rest_args = signature.positional_params.rest?
|
7
|
+
req_kwargs = signature.keyword_params.required
|
8
|
+
|
9
|
+
if signature.positional_args.size < req_args || (!rest_args && signature.positional_args.size > allowed_args)
|
10
|
+
expected_desc = if rest_args
|
11
|
+
"#{req_args}+"
|
12
|
+
elsif allowed_args != req_args
|
13
|
+
"#{req_args}..#{allowed_args}"
|
14
|
+
else
|
15
|
+
req_args.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
"wrong number of arguments (given #{signature.positional_args.size}, expected #{expected_desc}#{"; required keyword#{"s" if req_kwargs.size > 1}: #{req_kwargs.join(", ")}" unless req_kwargs.empty?})"
|
19
|
+
|
20
|
+
elsif !(missing_kwargs = req_kwargs.reject { |name| signature.keyword_args.key?(name) }).empty?
|
21
|
+
"missing keyword#{"s" if missing_kwargs.size > 1}: #{missing_kwargs.map { |name| name.inspect }.join(", ")}"
|
22
|
+
elsif !(unknown_kwargs = signature.keyword_args.keys.reject { |name| signature.keyword_params.all.include?(name) }).empty?
|
23
|
+
"unknown keyword#{"s" if unknown_kwargs.size > 1}: #{unknown_kwargs.map { |name| name.inspect }.join(", ")}"
|
24
|
+
else
|
25
|
+
"unknown cause (this is probably a bug in Mocktail)"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class TransformsParams
|
3
|
+
def transform(dry_call)
|
4
|
+
params = dry_call.original_method.parameters
|
5
|
+
|
6
|
+
Signature.new(
|
7
|
+
positional_params: Params.new(
|
8
|
+
all: params.select { |type, _|
|
9
|
+
[:req, :opt, :rest].include?(type)
|
10
|
+
}.map { |_, name| name },
|
11
|
+
required: params.select { |t, _| t == :req }.map { |_, n| n },
|
12
|
+
optional: params.select { |t, _| t == :opt }.map { |_, n| n },
|
13
|
+
rest: params.find { |type, _| type == :rest } & [1]
|
14
|
+
),
|
15
|
+
positional_args: dry_call.args,
|
16
|
+
|
17
|
+
keyword_params: Params.new(
|
18
|
+
all: params.select { |type, _|
|
19
|
+
[:keyreq, :key, :keyrest].include?(type)
|
20
|
+
}.map { |_, name| name },
|
21
|
+
required: params.select { |t, _| t == :keyreq }.map { |_, n| n },
|
22
|
+
optional: params.select { |t, _| t == :key }.map { |_, n| n },
|
23
|
+
rest: params.find { |type, _| type == :keyrest } & [1]
|
24
|
+
),
|
25
|
+
keyword_args: dry_call.kwargs,
|
26
|
+
|
27
|
+
block_param: params.find { |type, _| type == :block } & [1],
|
28
|
+
block_arg: dry_call.block
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative "simulates_argument_error/transforms_params"
|
2
|
+
require_relative "simulates_argument_error/reconciles_args_with_params"
|
3
|
+
require_relative "simulates_argument_error/recreates_message"
|
4
|
+
require_relative "simulates_argument_error/cleans_backtrace"
|
5
|
+
require_relative "share/stringifies_call"
|
6
|
+
|
7
|
+
module Mocktail
|
8
|
+
class SimulatesArgumentError
|
9
|
+
def initialize
|
10
|
+
@transforms_params = TransformsParams.new
|
11
|
+
@reconciles_args_with_params = ReconcilesArgsWithParams.new
|
12
|
+
@recreates_message = RecreatesMessage.new
|
13
|
+
@cleans_backtrace = CleansBacktrace.new
|
14
|
+
@stringifies_call = StringifiesCall.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def simulate(dry_call)
|
18
|
+
signature = @transforms_params.transform(dry_call)
|
19
|
+
|
20
|
+
unless @reconciles_args_with_params.reconcile(signature)
|
21
|
+
@cleans_backtrace.clean(
|
22
|
+
ArgumentError.new([
|
23
|
+
@recreates_message.recreate(signature),
|
24
|
+
"[Mocktail call: `#{@stringifies_call.stringify(dry_call)}']"
|
25
|
+
].join(" "))
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -37,5 +37,17 @@ module Mocktail
|
|
37
37
|
def demonstration_in_progress?
|
38
38
|
@demonstration_in_progress
|
39
39
|
end
|
40
|
+
|
41
|
+
def double_for_instance(thing)
|
42
|
+
@doubles.find { |double| double.dry_instance == thing }
|
43
|
+
end
|
44
|
+
|
45
|
+
def stubbings_for_double(double)
|
46
|
+
@stubbings.select { |stubbing| stubbing.recording.double == double.dry_instance }
|
47
|
+
end
|
48
|
+
|
49
|
+
def calls_for_double(double)
|
50
|
+
@calls.select { |call| call.double == double.dry_instance }
|
51
|
+
end
|
40
52
|
end
|
41
53
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Mocktail
|
2
|
-
class Double
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
2
|
+
class Double < Struct.new(
|
3
|
+
:original_type,
|
4
|
+
:dry_type,
|
5
|
+
:dry_instance,
|
6
|
+
:dry_methods,
|
7
|
+
keyword_init: true
|
8
|
+
)
|
10
9
|
end
|
11
10
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class Explanation
|
3
|
+
attr_reader :reference, :message
|
4
|
+
|
5
|
+
def initialize(reference, message)
|
6
|
+
@reference = reference
|
7
|
+
@message = message
|
8
|
+
end
|
9
|
+
|
10
|
+
def type
|
11
|
+
self.class
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NoExplanation < Explanation
|
16
|
+
end
|
17
|
+
|
18
|
+
class UnsatisfiedStubExplanation < Explanation
|
19
|
+
end
|
20
|
+
|
21
|
+
class DoubleExplanation < Explanation
|
22
|
+
end
|
23
|
+
|
24
|
+
class ReplacedTypeExplanation < Explanation
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class Signature < Struct.new(
|
3
|
+
:positional_params,
|
4
|
+
:positional_args,
|
5
|
+
:keyword_params,
|
6
|
+
:keyword_args,
|
7
|
+
:block_param,
|
8
|
+
:block_arg,
|
9
|
+
keyword_init: true
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Params < Struct.new(
|
14
|
+
:all,
|
15
|
+
:required,
|
16
|
+
:optional,
|
17
|
+
:rest,
|
18
|
+
keyword_init: true
|
19
|
+
)
|
20
|
+
|
21
|
+
def initialize(**params)
|
22
|
+
super
|
23
|
+
self.all ||= []
|
24
|
+
self.required ||= []
|
25
|
+
self.optional ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def allowed
|
29
|
+
required + optional
|
30
|
+
end
|
31
|
+
|
32
|
+
def rest?
|
33
|
+
!!rest
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mocktail
|
2
|
+
class StubReturnedNil < BasicObject
|
3
|
+
attr_reader :unsatisfied_stubbing
|
4
|
+
|
5
|
+
def initialize(unsatisfied_stubbing)
|
6
|
+
@unsatisfied_stubbing = unsatisfied_stubbing
|
7
|
+
end
|
8
|
+
|
9
|
+
def was_returned_by_unsatisfied_stub?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def tap
|
14
|
+
yield self
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, **kwargs, &blk)
|
19
|
+
nil.send(name, *args, **kwargs, &blk)
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(name, include_all = false)
|
23
|
+
nil.respond_to?(name, include_all)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,61 +1,60 @@
|
|
1
|
-
# The top shelf stores all cross-thread & thread-aware state, so anything that
|
2
|
-
# goes here is on its own when it comes to ensuring thread safety.
|
3
1
|
module Mocktail
|
4
2
|
class TopShelf
|
5
3
|
def self.instance
|
6
|
-
|
4
|
+
Thread.current[:mocktail_top_shelf] ||= new
|
7
5
|
end
|
8
6
|
|
7
|
+
@@type_replacements = {}
|
8
|
+
@@type_replacements_mutex = Mutex.new
|
9
|
+
|
9
10
|
def initialize
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@singleton_method_registrations = {}
|
11
|
+
@new_registrations = []
|
12
|
+
@of_next_registrations = []
|
13
|
+
@singleton_method_registrations = []
|
14
14
|
end
|
15
15
|
|
16
16
|
def type_replacement_for(type)
|
17
|
-
|
17
|
+
@@type_replacements_mutex.synchronize {
|
18
|
+
@@type_replacements[type] ||= TypeReplacement.new(type: type)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_replacement_if_exists_for(type)
|
23
|
+
@@type_replacements_mutex.synchronize {
|
24
|
+
@@type_replacements[type]
|
25
|
+
}
|
18
26
|
end
|
19
27
|
|
20
28
|
def reset_current_thread!
|
21
|
-
|
22
|
-
@of_next_registrations[Thread.current] = []
|
23
|
-
@singleton_method_registrations[Thread.current] = []
|
29
|
+
Thread.current[:mocktail_top_shelf] = self.class.new
|
24
30
|
end
|
25
31
|
|
26
32
|
def register_new_replacement!(type)
|
27
|
-
@new_registrations
|
28
|
-
@new_registrations[Thread.current] |= [type]
|
33
|
+
@new_registrations |= [type]
|
29
34
|
end
|
30
35
|
|
31
36
|
def new_replaced?(type)
|
32
|
-
@new_registrations
|
33
|
-
@new_registrations[Thread.current].include?(type)
|
37
|
+
@new_registrations.include?(type)
|
34
38
|
end
|
35
39
|
|
36
40
|
def register_of_next_replacement!(type)
|
37
|
-
@of_next_registrations
|
38
|
-
@of_next_registrations[Thread.current] |= [type]
|
41
|
+
@of_next_registrations |= [type]
|
39
42
|
end
|
40
43
|
|
41
44
|
def of_next_registered?(type)
|
42
|
-
@of_next_registrations
|
43
|
-
@of_next_registrations[Thread.current].include?(type)
|
45
|
+
@of_next_registrations.include?(type)
|
44
46
|
end
|
45
47
|
|
46
48
|
def unregister_of_next_replacement!(type)
|
47
|
-
@of_next_registrations
|
48
|
-
@of_next_registrations[Thread.current] -= [type]
|
49
|
+
@of_next_registrations -= [type]
|
49
50
|
end
|
50
51
|
|
51
52
|
def register_singleton_method_replacement!(type)
|
52
|
-
@singleton_method_registrations
|
53
|
-
@singleton_method_registrations[Thread.current] |= [type]
|
53
|
+
@singleton_method_registrations |= [type]
|
54
54
|
end
|
55
55
|
|
56
56
|
def singleton_methods_replaced?(type)
|
57
|
-
@singleton_method_registrations
|
58
|
-
@singleton_method_registrations[Thread.current].include?(type)
|
57
|
+
@singleton_method_registrations.include?(type)
|
59
58
|
end
|
60
59
|
end
|
61
60
|
end
|
data/lib/mocktail/value.rb
CHANGED
@@ -2,7 +2,13 @@ require_relative "value/cabinet"
|
|
2
2
|
require_relative "value/call"
|
3
3
|
require_relative "value/demo_config"
|
4
4
|
require_relative "value/double"
|
5
|
+
require_relative "value/double_data"
|
6
|
+
require_relative "value/explanation"
|
5
7
|
require_relative "value/matcher_registry"
|
8
|
+
require_relative "value/signature"
|
6
9
|
require_relative "value/stubbing"
|
10
|
+
require_relative "value/stub_returned_nil"
|
7
11
|
require_relative "value/top_shelf"
|
8
12
|
require_relative "value/type_replacement"
|
13
|
+
require_relative "value/type_replacement_data"
|
14
|
+
require_relative "value/unsatisfied_stubbing"
|
@@ -1,16 +1,18 @@
|
|
1
1
|
require_relative "raises_verification_error/gathers_calls_of_method"
|
2
|
-
require_relative "
|
2
|
+
require_relative "../share/stringifies_method_name"
|
3
|
+
require_relative "../share/stringifies_call"
|
3
4
|
|
4
5
|
module Mocktail
|
5
6
|
class RaisesVerificationError
|
6
7
|
def initialize
|
7
8
|
@gathers_calls_of_method = GathersCallsOfMethod.new
|
9
|
+
@stringifies_method_name = StringifiesMethodName.new
|
8
10
|
@stringifies_call = StringifiesCall.new
|
9
11
|
end
|
10
12
|
|
11
13
|
def raise(recording, verifiable_calls, demo_config)
|
12
14
|
Kernel.raise VerificationError.new <<~MSG
|
13
|
-
Expected mocktail of
|
15
|
+
Expected mocktail of `#{@stringifies_method_name.stringify(recording)}' to be called like:
|
14
16
|
|
15
17
|
#{@stringifies_call.stringify(recording)}#{[
|
16
18
|
(" [#{demo_config.times} #{pl("time", demo_config.times)}]" unless demo_config.times.nil?),
|
data/lib/mocktail/version.rb
CHANGED
data/lib/mocktail.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
require_relative "mocktail/dsl"
|
2
2
|
require_relative "mocktail/errors"
|
3
|
+
require_relative "mocktail/explains_thing"
|
3
4
|
require_relative "mocktail/handles_dry_call"
|
4
5
|
require_relative "mocktail/handles_dry_new_call"
|
5
6
|
require_relative "mocktail/imitates_type"
|
6
7
|
require_relative "mocktail/initializes_mocktail"
|
7
8
|
require_relative "mocktail/matcher_presentation"
|
8
9
|
require_relative "mocktail/matchers"
|
10
|
+
require_relative "mocktail/raises_neato_no_method_error"
|
9
11
|
require_relative "mocktail/registers_matcher"
|
10
12
|
require_relative "mocktail/registers_stubbing"
|
11
13
|
require_relative "mocktail/replaces_next"
|
12
14
|
require_relative "mocktail/replaces_type"
|
13
15
|
require_relative "mocktail/resets_state"
|
16
|
+
require_relative "mocktail/simulates_argument_error"
|
14
17
|
require_relative "mocktail/value"
|
15
18
|
require_relative "mocktail/verifies_call"
|
16
19
|
require_relative "mocktail/version"
|
@@ -54,7 +57,13 @@ module Mocktail
|
|
54
57
|
ResetsState.new.reset
|
55
58
|
end
|
56
59
|
|
60
|
+
def self.explain(thing)
|
61
|
+
ExplainsThing.new.explain(thing)
|
62
|
+
end
|
63
|
+
|
57
64
|
# Stores most transactional state about calls & stubbing configurations
|
65
|
+
# Anything returned by this is undocumented and could change at any time, so
|
66
|
+
# don't commit code that relies on it!
|
58
67
|
def self.cabinet
|
59
68
|
Thread.current[:mocktail_store] ||= Cabinet.new
|
60
69
|
end
|
data/mocktail.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ["Justin Searls"]
|
7
7
|
spec.email = ["searls@gmail.com"]
|
8
8
|
|
9
|
-
spec.summary = "your objects,
|
9
|
+
spec.summary = "Take your objects, and make them a double"
|
10
10
|
spec.homepage = "https://github.com/testdouble/mocktail"
|
11
11
|
spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
|
12
12
|
|