bogus 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/Guardfile +1 -3
- data/Guardfile.cucumber +0 -2
- data/README.md +1 -1
- data/bogus.gemspec +3 -1
- data/features/changelog.md +11 -0
- data/features/configuration/fake_ar_attributes.feature +2 -2
- data/features/contract_tests/contract_tests_mocks.feature +5 -5
- data/features/contract_tests/contract_tests_spies.feature +1 -1
- data/features/contract_tests/contract_tests_stubs.feature +6 -6
- data/features/contract_tests/custom_overwritten_class.feature +4 -4
- data/features/contract_tests/return_value_contracts.feature +2 -2
- data/features/fakes/anonymous_doubles.feature +5 -5
- data/features/fakes/duck_types.feature +9 -9
- data/features/fakes/fake_objects.feature +31 -8
- data/features/fakes/global_fake_configuration.feature +5 -5
- data/features/fakes/replacing_classes.feature +5 -5
- data/features/readme.md +5 -5
- data/features/safe_stubbing/argument_matchers.feature +23 -10
- data/features/safe_stubbing/safe_mocking.feature +1 -1
- data/features/safe_stubbing/safe_stubbing.feature +1 -1
- data/features/safe_stubbing/spies.feature +7 -7
- data/lib/bogus.rb +1 -0
- data/lib/bogus/core_ext.rb +22 -0
- data/lib/bogus/fakes/fake.rb +2 -1
- data/lib/bogus/fakes/method_stringifier.rb +1 -0
- data/lib/bogus/mocking_dsl.rb +4 -0
- data/lib/bogus/stubbing/has_overwritten_methods.rb +28 -10
- data/lib/bogus/stubbing/have_received_matcher.rb +4 -2
- data/lib/bogus/stubbing/matchers/any_args.rb +9 -0
- data/lib/bogus/stubbing/matchers/matches_argument.rb +11 -0
- data/lib/bogus/stubbing/{any_args.rb → matchers/with_arguments.rb} +0 -6
- data/lib/bogus/stubbing/overwrites_methods.rb +6 -1
- data/lib/bogus/stubbing/undefined_return_value.rb +4 -0
- data/lib/bogus/support.rb +11 -0
- data/lib/bogus/version.rb +1 -1
- data/spec/bogus/clean_ruby_spec.rb +1 -1
- data/spec/bogus/configuration_spec.rb +2 -2
- data/spec/bogus/contracts/adds_contract_verification_spec.rb +10 -10
- data/spec/bogus/contracts/adds_recording_spec.rb +4 -4
- data/spec/bogus/contracts/interactions_repository_spec.rb +15 -15
- data/spec/bogus/contracts/proxy_class_spec.rb +7 -7
- data/spec/bogus/contracts/records_double_interactions_spec.rb +1 -1
- data/spec/bogus/contracts/verifies_contracts_spec.rb +3 -3
- data/spec/bogus/fakes/base_class_identifier_spec.rb +1 -1
- data/spec/bogus/fakes/class_methods_spec.rb +4 -5
- data/spec/bogus/fakes/converts_name_to_class_spec.rb +3 -3
- data/spec/bogus/fakes/copies_classes_spec.rb +20 -16
- data/spec/bogus/fakes/creates_fakes_spec.rb +6 -6
- data/spec/bogus/fakes/creates_fakes_with_stubbed_methods_spec.rb +16 -15
- data/spec/bogus/fakes/fake_ar_attributes_spec.rb +3 -3
- data/spec/bogus/fakes/fake_configuration_spec.rb +16 -16
- data/spec/bogus/fakes/fake_registry_spec.rb +2 -2
- data/spec/bogus/fakes/fake_spec.rb +1 -1
- data/spec/bogus/fakes/fakes_classes_spec.rb +4 -4
- data/spec/bogus/fakes/faking_factories_spec.rb +4 -4
- data/spec/bogus/fakes/frozen_fakes_spec.rb +4 -4
- data/spec/bogus/fakes/instance_methods_spec.rb +5 -5
- data/spec/bogus/fakes/makes_ducks_spec.rb +3 -3
- data/spec/bogus/fakes/makes_substitute_methods_spec.rb +1 -1
- data/spec/bogus/fakes/overwriten_classes_spec.rb +3 -3
- data/spec/bogus/fakes/overwrites_classes_spec.rb +2 -2
- data/spec/bogus/fakes/registers_created_fakes_spec.rb +3 -3
- data/spec/bogus/fakes/resets_overwritten_classes_spec.rb +3 -3
- data/spec/bogus/fakes/stubbing_new_method_on_fake_class_spec.rb +25 -0
- data/spec/bogus/mocking_dsl_spec.rb +36 -36
- data/spec/bogus/ruby_2_1_support_spec.rb +38 -0
- data/spec/bogus/ruby_2_support_spec.rb +14 -64
- data/spec/bogus/stubbing/anything_spec.rb +5 -5
- data/spec/bogus/stubbing/double_spec.rb +2 -2
- data/spec/bogus/stubbing/have_received_matcher_spec.rb +11 -6
- data/spec/bogus/stubbing/interaction_spec.rb +7 -7
- data/spec/bogus/stubbing/multi_stubber_spec.rb +2 -2
- data/spec/bogus/stubbing/overwrites_methods_spec.rb +8 -8
- data/spec/bogus/stubbing/record_interactions_spec.rb +3 -3
- data/spec/bogus/stubbing/shadow_spec.rb +28 -28
- data/spec/bogus/stubbing/stubbing_existing_methods_on_fakes_spec.rb +50 -0
- data/spec/bogus/stubbing/tracks_existence_of_test_doubles_spec.rb +2 -2
- data/spec/bogus/stubbing/undefined_return_value_spec.rb +2 -2
- data/spec/spec_helper.rb +6 -1
- data/spec/support/shared_examples_for_keyword_arguments.rb +63 -0
- metadata +160 -160
- data/Gemfile.lock +0 -146
@@ -55,7 +55,7 @@ Feature: Replacing classes with fakes
|
|
55
55
|
{name: "Moby Dick", author: "Herman Melville"}]
|
56
56
|
File.open(Library::FILE, "w") { |f| f.print books.to_yaml }
|
57
57
|
|
58
|
-
Library.books.
|
58
|
+
expect(Library.books).to eq(books)
|
59
59
|
end
|
60
60
|
|
61
61
|
after do
|
@@ -77,7 +77,7 @@ Feature: Replacing classes with fakes
|
|
77
77
|
|
78
78
|
fake_class(Library, books: [tom_sawyer, moby_dick])
|
79
79
|
|
80
|
-
BookIndex.by_author("Mark Twain").
|
80
|
+
expect(BookIndex.by_author("Mark Twain")).to eq([tom_sawyer])
|
81
81
|
end
|
82
82
|
end
|
83
83
|
"""
|
@@ -99,7 +99,7 @@ Feature: Replacing classes with fakes
|
|
99
99
|
{name: "Moby Dick", author: "Herman Melville"}]
|
100
100
|
File.open(Library::FILE, "w") { |f| f.print books.to_yaml }
|
101
101
|
|
102
|
-
Library.books.
|
102
|
+
expect(Library.books).to eq(books)
|
103
103
|
end
|
104
104
|
|
105
105
|
after do
|
@@ -122,7 +122,7 @@ Feature: Replacing classes with fakes
|
|
122
122
|
fake_class(Library, fake_name: :book_repository,
|
123
123
|
books: [tom_sawyer, moby_dick])
|
124
124
|
|
125
|
-
BookIndex.by_author("Mark Twain").
|
125
|
+
expect(BookIndex.by_author("Mark Twain")).to eq([tom_sawyer])
|
126
126
|
end
|
127
127
|
end
|
128
128
|
"""
|
@@ -139,7 +139,7 @@ Feature: Replacing classes with fakes
|
|
139
139
|
fake_class(Library, books: [])
|
140
140
|
|
141
141
|
it "returns books written by author" do
|
142
|
-
BookIndex.by_author("Mark Twain").
|
142
|
+
expect(BookIndex.by_author("Mark Twain")).to eq([])
|
143
143
|
end
|
144
144
|
end
|
145
145
|
"""
|
data/features/readme.md
CHANGED
@@ -10,7 +10,7 @@ It is not uncommon to encounter code like this in isolated unit tests:
|
|
10
10
|
|
11
11
|
report_card = ReportCard.new(scores, students)
|
12
12
|
|
13
|
-
report_card.average_score.
|
13
|
+
expect(report_card.average_score).to eq(7)
|
14
14
|
end
|
15
15
|
|
16
16
|
_NOTE: In the above example we use mocha syntax, but this patten is common
|
@@ -42,7 +42,7 @@ Let's reexamine our previous example, this time Bogus-style:
|
|
42
42
|
|
43
43
|
report_card = ReportCard.new(scores, students)
|
44
44
|
|
45
|
-
report_card.average_score.
|
45
|
+
expect(report_card.average_score).to eq(7)
|
46
46
|
end
|
47
47
|
|
48
48
|
Can you spot the difference? Not much, huh?
|
@@ -73,7 +73,7 @@ Now if you test an object that collaborates with our `PushNotifier`, you would d
|
|
73
73
|
|
74
74
|
comment_adder.add("Hello world!")
|
75
75
|
|
76
|
-
push_notifier.
|
76
|
+
expect(push_notifier).to have_received.notify_async("Comment: 'Hello world!' added.")
|
77
77
|
end
|
78
78
|
|
79
79
|
While not really impressive, this feature is worth mentioning because it will eliminate a lot of the mocking and stubbing from your tests.
|
@@ -115,7 +115,7 @@ A contract test like that could look like this:
|
|
115
115
|
scores.add("John", 5)
|
116
116
|
scores.add("Mary", 9)
|
117
117
|
|
118
|
-
scores.get(["John", "Mary"]).
|
118
|
+
expect(scores.get(["John", "Mary"])).to eq([5, 9])
|
119
119
|
end
|
120
120
|
|
121
121
|
Obviously Bogus won't be able to write those tests for you. However it can remind you if you forget to add one.
|
@@ -155,7 +155,7 @@ You can test it easily, with all the benefits of fakes, safe stubbing and contra
|
|
155
155
|
it "should send a push notification" do
|
156
156
|
CommentAdder.add("the user", "the comment")
|
157
157
|
|
158
|
-
PushNotifier.
|
158
|
+
expect(PushNotifier).to have_received.notify_async("comment added")
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -18,7 +18,7 @@ Feature: Argument matchers
|
|
18
18
|
|
19
19
|
stub(Catalog).books_by_author_and_title(any_args) { :some_book }
|
20
20
|
|
21
|
-
Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes").
|
21
|
+
expect(Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes")).to eq(:some_book)
|
22
22
|
"""
|
23
23
|
|
24
24
|
Scenario: Stubbing methods with some wildcard arguments
|
@@ -29,12 +29,12 @@ Feature: Argument matchers
|
|
29
29
|
stub(Catalog).books_by_author_and_title(any_args) { :some_book }
|
30
30
|
stub(Catalog).books_by_author_and_title("Mark Twain", anything) { :twains_book }
|
31
31
|
|
32
|
-
Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer").
|
33
|
-
Catalog.books_by_author_and_title("Mark Twain", "Huckleberry Finn").
|
34
|
-
Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes").
|
32
|
+
expect(Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer")).to eq(:twains_book)
|
33
|
+
expect(Catalog.books_by_author_and_title("Mark Twain", "Huckleberry Finn")).to eq(:twains_book)
|
34
|
+
expect(Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes")).to eq(:some_book)
|
35
35
|
"""
|
36
36
|
|
37
|
-
Scenario: Stubbing methods with proc
|
37
|
+
Scenario: Stubbing methods with proc arguments matcher
|
38
38
|
Then the following test should pass:
|
39
39
|
"""ruby
|
40
40
|
require_relative 'catalog'
|
@@ -42,9 +42,22 @@ Feature: Argument matchers
|
|
42
42
|
stub(Catalog).books_by_author_and_title(any_args) { :some_book }
|
43
43
|
stub(Catalog).books_by_author_and_title(with{|author| author =~ /Twain/ }) { :twains_book }
|
44
44
|
|
45
|
-
Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer").
|
46
|
-
Catalog.books_by_author_and_title("M. Twain", "Huckleberry Finn").
|
47
|
-
Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes").
|
45
|
+
expect(Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer")).to eq(:twains_book)
|
46
|
+
expect(Catalog.books_by_author_and_title("M. Twain", "Huckleberry Finn")).to eq(:twains_book)
|
47
|
+
expect(Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes")).to eq(:some_book)
|
48
|
+
"""
|
49
|
+
|
50
|
+
Scenario: Stubbing methods with proc argument matcher
|
51
|
+
Then the following test should pass:
|
52
|
+
"""ruby
|
53
|
+
require_relative 'catalog'
|
54
|
+
|
55
|
+
stub(Catalog).books_by_author_and_title(any_args) { :some_book }
|
56
|
+
stub(Catalog).books_by_author_and_title(matches{|author| author =~ /Twain/ }, "Tom Sawyer") { :twains_book }
|
57
|
+
|
58
|
+
expect(Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer")).to eq(:twains_book)
|
59
|
+
expect(Catalog.books_by_author_and_title("M. Twain", "Huckleberry Finn")).to eq(:some_book)
|
60
|
+
expect(Catalog.books_by_author_and_title("Arthur Conan Doyle", "Sherlock Holmes")).to eq(:some_book)
|
48
61
|
"""
|
49
62
|
|
50
63
|
Scenario: Stubbing methods with argument type matcher
|
@@ -55,6 +68,6 @@ Feature: Argument matchers
|
|
55
68
|
stub(Catalog).books_by_author_and_title(any_args) { :some_book }
|
56
69
|
stub(Catalog).books_by_author_and_title(any(String), any(String)) { :twains_book }
|
57
70
|
|
58
|
-
Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer").
|
59
|
-
Catalog.books_by_author_and_title("M. Twain", :other_book).
|
71
|
+
expect(Catalog.books_by_author_and_title("Mark Twain", "Tom Sawyer")).to eq(:twains_book)
|
72
|
+
expect(Catalog.books_by_author_and_title("M. Twain", :other_book)).to eq(:some_book)
|
60
73
|
"""
|
@@ -43,8 +43,8 @@ Feature: Spies
|
|
43
43
|
|
44
44
|
student.study("Moby Dick", "Sherlock Holmes")
|
45
45
|
|
46
|
-
library.
|
47
|
-
library.
|
46
|
+
expect(library).to have_received.checkout("Moby Dick")
|
47
|
+
expect(library).to have_received.checkout("Sherlock Holmes")
|
48
48
|
end
|
49
49
|
end
|
50
50
|
"""
|
@@ -63,7 +63,7 @@ Feature: Spies
|
|
63
63
|
|
64
64
|
student.study("Moby Dick")
|
65
65
|
|
66
|
-
library.
|
66
|
+
expect(library).not_to have_received.return_book("Moby Dick")
|
67
67
|
end
|
68
68
|
end
|
69
69
|
"""
|
@@ -82,7 +82,7 @@ Feature: Spies
|
|
82
82
|
|
83
83
|
student.study("Moby Dick", "Sherlock Holmes")
|
84
84
|
|
85
|
-
library.
|
85
|
+
expect(library).not_to have_received.checkout("Moby Dick",
|
86
86
|
"Sherlock Holmes")
|
87
87
|
end
|
88
88
|
end
|
@@ -100,9 +100,9 @@ Feature: Spies
|
|
100
100
|
it "studies using books from library" do
|
101
101
|
stub(library).checkout("Moby Dick") { "checked out" }
|
102
102
|
|
103
|
-
library.checkout("Moby Dick").
|
103
|
+
expect(library.checkout("Moby Dick")).to eq("checked out")
|
104
104
|
|
105
|
-
library.
|
105
|
+
expect(library).to have_received.checkout("Moby Dick")
|
106
106
|
end
|
107
107
|
end
|
108
108
|
"""
|
@@ -138,7 +138,7 @@ Feature: Spies
|
|
138
138
|
it "sets the background to red" do
|
139
139
|
Popup.alert("No such file!", canvas)
|
140
140
|
|
141
|
-
canvas.
|
141
|
+
expect(canvas).to have_received(:background_color=, "red")
|
142
142
|
end
|
143
143
|
end
|
144
144
|
"""
|
data/lib/bogus.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative 'bogus/stubbing/record_interactions'
|
|
7
7
|
require_relative 'bogus/proxies_method_calls'
|
8
8
|
require_relative 'bogus/rspec/extensions'
|
9
9
|
require_relative 'bogus/rspec/adapter'
|
10
|
+
require_relative 'bogus/support'
|
10
11
|
|
11
12
|
dir = File.realpath File.expand_path('../bogus', __FILE__)
|
12
13
|
Dir["#{dir}/**/*.rb"].sort.each do |path|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This monkey patch should not break Ruby compatibility
|
2
|
+
# but is necessary because MRI, insead of calling object.kind_of?(module),
|
3
|
+
# calls the C function, which implements kind_of. This makes
|
4
|
+
# using fake objects in switch cases produce unexpected results:
|
5
|
+
#
|
6
|
+
# fake = fake(:library) { Library }
|
7
|
+
#
|
8
|
+
# case fake
|
9
|
+
# when Library then "bingo!"
|
10
|
+
# else raise "oh noes!"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Without the patch, the snippet above raises 'oh noes!'
|
14
|
+
# instead of returning 'bingo!'.
|
15
|
+
class Module
|
16
|
+
# don't warn about redefining === method
|
17
|
+
Bogus::Support.supress_warnings do
|
18
|
+
def ===(object)
|
19
|
+
object.kind_of?(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/bogus/fakes/fake.rb
CHANGED
@@ -29,6 +29,7 @@ module Bogus
|
|
29
29
|
case type
|
30
30
|
when :block then "&#{name}"
|
31
31
|
when :key then default ? "#{name}: #{default}" : "#{name}: #{name}"
|
32
|
+
when :keyreq then default ? "#{name}:" : "#{name}: #{name}"
|
32
33
|
when :opt then default ? "#{name} = #{default}" : name
|
33
34
|
when :req then name
|
34
35
|
when :rest then "*#{name}"
|
data/lib/bogus/mocking_dsl.rb
CHANGED
@@ -1,24 +1,42 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Bogus
|
2
4
|
module HasOverwritenMethods
|
5
|
+
def self.aliased_name(name)
|
6
|
+
:"__bogus__alias__#{name}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.alias(object, new_name, name)
|
10
|
+
object.singleton_class.send(:alias_method, new_name, name)
|
11
|
+
end
|
12
|
+
|
3
13
|
def __overwritten_methods__
|
4
|
-
@__overwritten_methods__ ||=
|
14
|
+
@__overwritten_methods__ ||= Set.new
|
5
15
|
end
|
6
16
|
|
7
17
|
def __overwrite__(name, method, body)
|
8
|
-
return if __overwritten_methods__
|
9
|
-
|
10
|
-
|
18
|
+
return if __overwritten_methods__.include?(name)
|
19
|
+
|
20
|
+
new_name = HasOverwritenMethods.aliased_name(name)
|
21
|
+
HasOverwritenMethods.alias(self, new_name, name) if method
|
22
|
+
|
23
|
+
__overwritten_methods__ << name
|
24
|
+
|
11
25
|
instance_eval(body)
|
12
26
|
end
|
13
27
|
|
14
28
|
def __reset__
|
15
|
-
__overwritten_methods__.each do |name
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
29
|
+
__overwritten_methods__.each do |name|
|
30
|
+
new_name = HasOverwritenMethods.aliased_name(name)
|
31
|
+
|
32
|
+
if respond_to?(new_name)
|
33
|
+
HasOverwritenMethods.alias(self, name, new_name)
|
34
|
+
instance_eval "undef #{new_name}"
|
35
|
+
else
|
36
|
+
instance_eval "undef #{name}"
|
37
|
+
end
|
20
38
|
end
|
21
|
-
@__overwritten_methods__ =
|
39
|
+
@__overwritten_methods__ = nil
|
22
40
|
@__shadow__ = nil
|
23
41
|
end
|
24
42
|
end
|
@@ -21,15 +21,17 @@ module Bogus
|
|
21
21
|
"have received #{call_str(@name, @args)}"
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def failure_message
|
25
25
|
return NO_SHADOW unless Shadow.has_shadow?(@subject)
|
26
26
|
%Q{Expected #{@subject.inspect} to #{description}, but it didn't.\n\n} + all_calls_str
|
27
27
|
end
|
28
|
+
alias_method :failure_message_for_should, :failure_message
|
28
29
|
|
29
|
-
def
|
30
|
+
def failure_message_when_negated
|
30
31
|
return NO_SHADOW unless Shadow.has_shadow?(@subject)
|
31
32
|
%Q{Expected #{@subject.inspect} not to #{description}, but it did.}
|
32
33
|
end
|
34
|
+
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
33
35
|
|
34
36
|
def method_call
|
35
37
|
proxy(:set_method)
|
@@ -6,7 +6,7 @@ module Bogus
|
|
6
6
|
|
7
7
|
def overwrite(object, name)
|
8
8
|
raise "wut?" if name == :__shadow__
|
9
|
-
return if
|
9
|
+
return if already_delegates_to_shadow?(object, name)
|
10
10
|
|
11
11
|
object.extend RecordInteractions
|
12
12
|
object.extend HasOverwritenMethods
|
@@ -25,6 +25,11 @@ module Bogus
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
+
def already_delegates_to_shadow?(object, name)
|
29
|
+
return false unless object.is_a?(FakeObject)
|
30
|
+
!Fake.instance_methods.include?(name)
|
31
|
+
end
|
32
|
+
|
28
33
|
def method_by_name(object, name)
|
29
34
|
object.method(name) if object.methods.include?(name)
|
30
35
|
end
|
data/lib/bogus/version.rb
CHANGED