-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathmocktail.rb
154 lines (134 loc) · 5.29 KB
/
mocktail.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# typed: strict
require "pathname"
if defined?(Mocktail::TYPED)
if eval("Mocktail::TYPED", binding, __FILE__, __LINE__)
warn "`require \"mocktail\"' was called, but Mocktail was already required as `require \"mocktail/sorbet\"', so we're NOT going to load it to avoid constants from being redefined. If you want to use Mocktail WITHOUT sorbet runtime checks, remove whatever is requiring `mocktail/sorbet'."
else
warn "`require \"mocktail/sorbet\"' was called, but Mocktail was already required as `require \"mocktail\"', so we're NOT going to load it to avoid constants from being redefined. If you want to use Mocktail WITH sorbet runtime checks, remove whatever is requiring `mocktail'."
end
return
end
require_relative "mocktail/initialize_based_on_type_system_mode_switching"
require_relative "mocktail/collects_calls"
require_relative "mocktail/debug"
require_relative "mocktail/dsl"
require_relative "mocktail/errors"
require_relative "mocktail/explains_thing"
require_relative "mocktail/explains_nils"
require_relative "mocktail/grabs_original_method_parameters"
require_relative "mocktail/handles_dry_call"
require_relative "mocktail/handles_dry_new_call"
require_relative "mocktail/imitates_type"
require_relative "mocktail/initializes_mocktail"
require_relative "mocktail/matcher_presentation"
require_relative "mocktail/matchers"
require_relative "mocktail/raises_neato_no_method_error"
require_relative "mocktail/registers_matcher"
require_relative "mocktail/registers_stubbing"
require_relative "mocktail/replaces_next"
require_relative "mocktail/replaces_type"
require_relative "mocktail/resets_state"
require_relative "mocktail/simulates_argument_error"
require_relative "mocktail/stringifies_method_signature"
require_relative "mocktail/value"
require_relative "mocktail/verifies_call"
require_relative "mocktail/version"
module Mocktail
extend T::Sig
extend DSL
BASE_PATH = T.let((Pathname.new(__FILE__) + "..").to_s, String)
# Returns an instance of `type` whose implementation is mocked out
sig {
type_parameters(:T)
.params(type: T::Class[T.all(T.type_parameter(:T), Object)])
.returns(T.all(T.type_parameter(:T), Object))
}
def self.of(type)
ImitatesType.new.imitate(type)
end
# Returns an instance of `klass` whose implementation is mocked out AND
# stubs its constructor to return that fake the next time klass.new is called
sig {
type_parameters(:T)
.params(type: T::Class[T.all(T.type_parameter(:T), Object)], count: T.nilable(Integer))
.returns(T.type_parameter(:T))
}
def self.of_next(type, count: 1)
count ||= 1
if count == 1
ReplacesNext.new.replace_once(type)
elsif !T.unsafe(Mocktail::TYPED) || T::Private::RuntimeLevels.default_checked_level == :never
T.unsafe(ReplacesNext.new).replace(type, count)
else
raise TypeCheckingError.new <<~MSG
Calling `Mocktail.of_next()' with a `count' value other than 1 is not supported when
type checking is enabled. There are two ways to fix this:
1. Use `Mocktail.of_next_with_count(type, count)' instead, which will always return
an array of fake objects.
2. Disable runtime type checking by setting `T::Private::RuntimeLevels.default_checked_level = :never'
or by setting the envronment variable `SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL=never'
MSG
end
end
# An alias of of_next that always returns an array of fakes
sig {
type_parameters(:T)
.params(type: T::Class[T.all(T.type_parameter(:T), Object)], count: Integer)
.returns(T::Array[T.type_parameter(:T)])
}
def self.of_next_with_count(type, count)
ReplacesNext.new.replace(type, count)
end
sig { returns(Mocktail::MatcherPresentation) }
def self.matchers
MatcherPresentation.new
end
sig { returns(Mocktail::Matchers::Captor) }
def self.captor
Matchers::Captor.new
end
sig { params(matcher: T.class_of(Mocktail::Matchers::Base)).void }
def self.register_matcher(matcher)
RegistersMatcher.new.register(matcher)
end
# Replaces every singleton method on `type` with a fake, and when instantiated
# or included will also fake instance methods
sig { params(type: T.any(T::Class[T.anything], Module)).void }
def self.replace(type)
ReplacesType.new.replace(type)
nil
end
sig { void }
def self.reset
ResetsState.new.reset
end
sig {
params(thing: Object)
.returns(Explanation)
}
def self.explain(thing)
ExplainsThing.new.explain(thing)
end
sig { returns(T::Array[Mocktail::UnsatisfyingCallExplanation]) }
def self.explain_nils
ExplainsNils.new.explain
end
# An alias for Mocktail.explain(double).reference.calls
# Takes an optional second parameter of the method name to filter only
# calls to that method
sig {
params(double: Object, method_name: T.nilable(T.any(String, Symbol)))
.returns(T::Array[Mocktail::Call])
}
def self.calls(double, method_name = nil)
CollectsCalls.new.collect(double, method_name&.to_sym)
end
# Stores most transactional state about calls & stubbing configurations
# Anything returned by this is undocumented and could change at any time, so
# don't commit code that relies on it!
sig { returns Cabinet }
def self.cabinet
Thread.current[:mocktail_store] ||= Cabinet.new
end
end
Mocktail::InitializesMocktail.new.init