bogus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.pelusa.yml +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +75 -0
- data/Guardfile +15 -0
- data/README.md +272 -0
- data/Rakefile +1 -0
- data/bogus.gemspec +34 -0
- data/features/configuration_options.feature +43 -0
- data/features/contract_tests_mocks.feature +100 -0
- data/features/contract_tests_spies.feature +72 -0
- data/features/contract_tests_stubs.feature +101 -0
- data/features/fake_objects.feature +94 -0
- data/features/return_value_contracts.feature +90 -0
- data/features/safe_mocking.feature +59 -0
- data/features/safe_stubbing.feature +62 -0
- data/features/spies.feature +78 -0
- data/features/step_definitions/rspec_steps.rb +68 -0
- data/features/support/env.rb +1 -0
- data/lib/bogus.rb +35 -0
- data/lib/bogus/adds_recording.rb +11 -0
- data/lib/bogus/configuration.rb +9 -0
- data/lib/bogus/contract_not_fulfilled.rb +24 -0
- data/lib/bogus/converts_name_to_class.rb +31 -0
- data/lib/bogus/copies_classes.rb +44 -0
- data/lib/bogus/creates_fakes.rb +32 -0
- data/lib/bogus/double.rb +10 -0
- data/lib/bogus/fake.rb +33 -0
- data/lib/bogus/fake_registry.rb +13 -0
- data/lib/bogus/injector.rb +64 -0
- data/lib/bogus/interaction.rb +24 -0
- data/lib/bogus/interaction_presenter.rb +29 -0
- data/lib/bogus/interactions_repository.rb +19 -0
- data/lib/bogus/invocation_matcher.rb +27 -0
- data/lib/bogus/method_stringifier.rb +31 -0
- data/lib/bogus/overwrites_classes.rb +9 -0
- data/lib/bogus/proxy_class.rb +22 -0
- data/lib/bogus/public_methods.rb +46 -0
- data/lib/bogus/record_interactions.rb +17 -0
- data/lib/bogus/recording_proxy.rb +18 -0
- data/lib/bogus/records_double_interactions.rb +11 -0
- data/lib/bogus/registers_created_fakes.rb +11 -0
- data/lib/bogus/rr_proxy.rb +5 -0
- data/lib/bogus/rspec.rb +4 -0
- data/lib/bogus/rspec_extensions.rb +32 -0
- data/lib/bogus/takes.rb +8 -0
- data/lib/bogus/verifies_contracts.rb +12 -0
- data/lib/bogus/verifies_stub_definition.rb +37 -0
- data/lib/bogus/version.rb +3 -0
- data/pelusa.sh +3 -0
- data/rbs.sh +3 -0
- data/spec/bogus/adds_recording_spec.rb +33 -0
- data/spec/bogus/configuration_spec.rb +17 -0
- data/spec/bogus/converts_name_to_class_spec.rb +40 -0
- data/spec/bogus/copies_classes_spec.rb +175 -0
- data/spec/bogus/creates_fakes_spec.rb +59 -0
- data/spec/bogus/double_spec.rb +31 -0
- data/spec/bogus/fake_registry_spec.rb +24 -0
- data/spec/bogus/interaction_spec.rb +68 -0
- data/spec/bogus/interactions_repository_spec.rb +50 -0
- data/spec/bogus/invocation_matcher_spec.rb +26 -0
- data/spec/bogus/mocking_dsl_spec.rb +76 -0
- data/spec/bogus/overwrites_classes_spec.rb +26 -0
- data/spec/bogus/proxy_class_spec.rb +95 -0
- data/spec/bogus/record_interactions_spec.rb +27 -0
- data/spec/bogus/records_double_interactions_spec.rb +25 -0
- data/spec/bogus/registers_created_fakes_spec.rb +25 -0
- data/spec/bogus/verifies_contracts_spec.rb +42 -0
- data/spec/bogus/verifies_stub_definition_spec.rb +71 -0
- data/spec/spec_helper.rb +14 -0
- metadata +299 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
Feature: Contract tests with mocks
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a file named "foo.rb" with:
|
5
|
+
"""ruby
|
6
|
+
class Library
|
7
|
+
def initialize
|
8
|
+
@books = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_book?(book)
|
12
|
+
@books.include?(book)
|
13
|
+
end
|
14
|
+
|
15
|
+
def checkout(book)
|
16
|
+
@books.delete(book)
|
17
|
+
end
|
18
|
+
|
19
|
+
def return(book)
|
20
|
+
@books << book
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Student
|
25
|
+
def read(book, library = Library.new)
|
26
|
+
if library.has_book?(book)
|
27
|
+
library.checkout(book)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
"""
|
32
|
+
And a spec file named "student_spec.rb" with:
|
33
|
+
"""ruby
|
34
|
+
describe Student do
|
35
|
+
fake(:library)
|
36
|
+
|
37
|
+
it "checks out the book from library if it is available" do
|
38
|
+
student = Student.new
|
39
|
+
mock(library).has_book?("Moby Dick") { true }
|
40
|
+
mock(library).checkout("Moby Dick") { "Moby Dick" }
|
41
|
+
|
42
|
+
student.read("Moby Dick", library)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "does not check out the book from library if not available" do
|
46
|
+
student = Student.new
|
47
|
+
mock(library).has_book?("Moby Dick") { false }
|
48
|
+
dont_allow(library).checkout("Moby Dick")
|
49
|
+
|
50
|
+
student.read("Moby Dick", library)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
"""
|
54
|
+
|
55
|
+
Scenario: Fails when mocked methods are not called on real object
|
56
|
+
Then spec file with following content should fail:
|
57
|
+
"""ruby
|
58
|
+
describe Library do
|
59
|
+
verify_contract(:library)
|
60
|
+
|
61
|
+
let(:library) { Library.new }
|
62
|
+
|
63
|
+
it "marks books as unavailable after they are checked out" do
|
64
|
+
library.return("Moby Dick")
|
65
|
+
|
66
|
+
library.checkout("Moby Dick")
|
67
|
+
|
68
|
+
library.has_book?("Moby Dick").should be_false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
"""
|
72
|
+
|
73
|
+
Scenario: Verifies that mocked methods are called
|
74
|
+
Then spec file with following content should pass:
|
75
|
+
"""ruby
|
76
|
+
describe Library do
|
77
|
+
verify_contract(:library)
|
78
|
+
|
79
|
+
let(:library) { Library.new }
|
80
|
+
|
81
|
+
it "allows checking out books that are in the inventory" do
|
82
|
+
library.return("Moby Dick")
|
83
|
+
|
84
|
+
library.has_book?("Moby Dick").should be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "does not allow checking out unavailable books" do
|
88
|
+
library.has_book?("Moby Dick").should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it "marks books as unavailable after they are checked out" do
|
92
|
+
library.return("Moby Dick")
|
93
|
+
|
94
|
+
library.checkout("Moby Dick")
|
95
|
+
|
96
|
+
library.has_book?("Moby Dick").should be_false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
"""
|
100
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
Feature: Contract tests with spies
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a file named "foo.rb" with:
|
5
|
+
"""ruby
|
6
|
+
class Library
|
7
|
+
def checkout(book)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Student
|
12
|
+
def read(book, library = Libarary.new)
|
13
|
+
library.checkout(book)
|
14
|
+
# ...
|
15
|
+
end
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
And a spec file named "student_spec.rb" with:
|
19
|
+
"""ruby
|
20
|
+
describe Student do
|
21
|
+
fake(:library)
|
22
|
+
|
23
|
+
it "reads books from library" do
|
24
|
+
student = Student.new
|
25
|
+
|
26
|
+
student.read("Moby Dick", library)
|
27
|
+
|
28
|
+
library.should have_received.checkout("Moby Dick")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
"""
|
32
|
+
|
33
|
+
Scenario: Stubbing methods that exist on real object
|
34
|
+
Then spec file with following content should pass:
|
35
|
+
"""ruby
|
36
|
+
describe Library do
|
37
|
+
verify_contract(:library)
|
38
|
+
|
39
|
+
it "checks out books" do
|
40
|
+
library = Library.new
|
41
|
+
|
42
|
+
library.checkout("Moby Dick")
|
43
|
+
|
44
|
+
# ...
|
45
|
+
end
|
46
|
+
end
|
47
|
+
"""
|
48
|
+
|
49
|
+
Scenario: Verifing that stubbed methods are tested
|
50
|
+
Then spec file with following content should fail:
|
51
|
+
"""ruby
|
52
|
+
describe Library do
|
53
|
+
verify_contract(:library)
|
54
|
+
|
55
|
+
end
|
56
|
+
"""
|
57
|
+
|
58
|
+
Scenario: Verifying that methods are tested with right arguments
|
59
|
+
Then spec file with following content should fail:
|
60
|
+
"""ruby
|
61
|
+
describe Library do
|
62
|
+
verify_contract(:library)
|
63
|
+
|
64
|
+
it "checks out books" do
|
65
|
+
library = Library.new
|
66
|
+
|
67
|
+
library.checkout("Moby Dick 2: The ulitmate")
|
68
|
+
|
69
|
+
# ...
|
70
|
+
end
|
71
|
+
end
|
72
|
+
"""
|
@@ -0,0 +1,101 @@
|
|
1
|
+
Feature: Contract tests with stubs
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a file named "foo.rb" with:
|
5
|
+
"""ruby
|
6
|
+
class Library
|
7
|
+
def initialize
|
8
|
+
@books = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_book?(book)
|
12
|
+
@books.include?(book)
|
13
|
+
end
|
14
|
+
|
15
|
+
def checkout(book)
|
16
|
+
@books.delete(book)
|
17
|
+
end
|
18
|
+
|
19
|
+
def return(book)
|
20
|
+
@books << book
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Student
|
25
|
+
def read(book, library = Library.new)
|
26
|
+
if library.has_book?(book)
|
27
|
+
library.checkout(book)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
"""
|
32
|
+
And a spec file named "student_spec.rb" with:
|
33
|
+
"""ruby
|
34
|
+
describe Student do
|
35
|
+
fake(:library)
|
36
|
+
|
37
|
+
it "checks out the book from library if it is available" do
|
38
|
+
student = Student.new
|
39
|
+
stub(library).has_book?("Moby Dick") { true }
|
40
|
+
|
41
|
+
student.read("Moby Dick", library)
|
42
|
+
|
43
|
+
library.should have_received.checkout("Moby Dick")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not check out the book from library if not available" do
|
47
|
+
student = Student.new
|
48
|
+
stub(library).has_book?("Moby Dick") { false }
|
49
|
+
|
50
|
+
student.read("Moby Dick", library)
|
51
|
+
|
52
|
+
library.should_not have_received.checkout("Moby Dick")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
"""
|
56
|
+
|
57
|
+
Scenario: Fails when stubbed methods are not called on real object
|
58
|
+
Then spec file with following content should fail:
|
59
|
+
"""ruby
|
60
|
+
describe Library do
|
61
|
+
verify_contract(:library)
|
62
|
+
|
63
|
+
let(:library) { Library.new }
|
64
|
+
|
65
|
+
it "marks books as unavailable after they are checked out" do
|
66
|
+
library.return("Moby Dick")
|
67
|
+
|
68
|
+
library.checkout("Moby Dick")
|
69
|
+
|
70
|
+
library.has_book?("Moby Dick").should be_false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
"""
|
74
|
+
|
75
|
+
Scenario: Verifies that stubbed methods are called
|
76
|
+
Then spec file with following content should pass:
|
77
|
+
"""ruby
|
78
|
+
describe Library do
|
79
|
+
verify_contract(:library)
|
80
|
+
|
81
|
+
let(:library) { Library.new }
|
82
|
+
|
83
|
+
it "allows checking out books that are in the inventory" do
|
84
|
+
library.return("Moby Dick")
|
85
|
+
|
86
|
+
library.has_book?("Moby Dick").should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "does not allow checking out unavailable books" do
|
90
|
+
library.has_book?("Moby Dick").should be_false
|
91
|
+
end
|
92
|
+
|
93
|
+
it "marks books as unavailable after they are checked out" do
|
94
|
+
library.return("Moby Dick")
|
95
|
+
|
96
|
+
library.checkout("Moby Dick")
|
97
|
+
|
98
|
+
library.has_book?("Moby Dick").should be_false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
"""
|
@@ -0,0 +1,94 @@
|
|
1
|
+
Feature: Faking existing classes
|
2
|
+
|
3
|
+
Bogus makes it easy to create fakes, which behave like a null-object and
|
4
|
+
have the same interface as the object being faked.
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a file named "foo.rb" with:
|
8
|
+
"""ruby
|
9
|
+
class Library
|
10
|
+
def checkout(book)
|
11
|
+
end
|
12
|
+
|
13
|
+
def return_book(book)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.look_up(book)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Student
|
21
|
+
def self.learn(library = Library.new)
|
22
|
+
library.checkout("hello world")
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
"""
|
27
|
+
|
28
|
+
Scenario: Calling methods that exist on real object
|
29
|
+
Then spec file with following content should pass:
|
30
|
+
"""ruby
|
31
|
+
describe Student do
|
32
|
+
fake(:library)
|
33
|
+
|
34
|
+
it "does something" do
|
35
|
+
Student.learn(library).should be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
"""
|
39
|
+
|
40
|
+
Scenario: Fakes have null-object semantics
|
41
|
+
Then spec file with following content should pass:
|
42
|
+
"""ruby
|
43
|
+
describe "library fake" do
|
44
|
+
fake(:library)
|
45
|
+
|
46
|
+
it "returns self from all methods" do
|
47
|
+
library.checkout("hello").should == library
|
48
|
+
end
|
49
|
+
|
50
|
+
it "makes method chaining possible" do
|
51
|
+
library.checkout("hello").return_book("world").should == library
|
52
|
+
end
|
53
|
+
end
|
54
|
+
"""
|
55
|
+
|
56
|
+
Scenario: Taking the guesswork out of finding a class to copy
|
57
|
+
Then spec file with following content should pass:
|
58
|
+
"""ruby
|
59
|
+
class PublicLibrary
|
60
|
+
def checkout(book)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "logger fake" do
|
65
|
+
fake(:library) { PublicLibrary }
|
66
|
+
|
67
|
+
it "uses the class provided in block instead of the guessed one" do
|
68
|
+
library.class.name.should == "PublicLibrary"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
"""
|
72
|
+
|
73
|
+
Scenario: Fakes which are classes
|
74
|
+
Then spec file with following content should pass:
|
75
|
+
"""ruby
|
76
|
+
describe "library class fake" do
|
77
|
+
fake(:library, as: :class)
|
78
|
+
|
79
|
+
it "is a class" do
|
80
|
+
library.should be_a(Class)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has the same name as original class" do
|
84
|
+
library.name.should == Library.name
|
85
|
+
end
|
86
|
+
|
87
|
+
it "has same methods as original class" do
|
88
|
+
library.look_up('something')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
"""
|
92
|
+
|
93
|
+
Scenario: Fakes which are objects
|
94
|
+
Given pending
|
@@ -0,0 +1,90 @@
|
|
1
|
+
Feature: Return value contracts
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a file named "foo.rb" with:
|
5
|
+
"""ruby
|
6
|
+
class SessionController
|
7
|
+
def initialize(authentication_service)
|
8
|
+
@authentication_service = authentication_service
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(params)
|
12
|
+
@authentication_service.authenticate(params[:login], params[:password])
|
13
|
+
:render_welcome
|
14
|
+
rescue
|
15
|
+
:render_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class WrongPassword < StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
class AuthenticationService
|
23
|
+
def initialize(user_db)
|
24
|
+
@user_db = user_db
|
25
|
+
end
|
26
|
+
|
27
|
+
def authenticate(login, password)
|
28
|
+
unless @user_db[login] == Digest::SHA1.hexdigest(password)
|
29
|
+
raise WrongPassword
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
"""
|
34
|
+
And a spec file named "session_controller_spec.rb" with:
|
35
|
+
"""ruby
|
36
|
+
describe SessionController do
|
37
|
+
fake(:authentication_service)
|
38
|
+
let(:controller) { SessionController.new(authentication_service) }
|
39
|
+
|
40
|
+
it "logs in the user with valid data" do
|
41
|
+
stub(authentication_service).authenticate('foo', 'bar')
|
42
|
+
|
43
|
+
controller.create(login: 'foo', password: 'bar').should == :render_welcome
|
44
|
+
end
|
45
|
+
|
46
|
+
it "fails with invalid data" do
|
47
|
+
stub(authentication_service).authenticate('baz', 'bar') { raise WrongPassword }
|
48
|
+
|
49
|
+
controller.create(login: 'baz', password: 'bar').should == :render_error
|
50
|
+
end
|
51
|
+
end
|
52
|
+
"""
|
53
|
+
|
54
|
+
Scenario: Fails when returned values differ
|
55
|
+
Then spec file with following content should fail:
|
56
|
+
"""ruby
|
57
|
+
describe AuthenticationService do
|
58
|
+
verify_contract(:authentication_service)
|
59
|
+
|
60
|
+
it "logs in the user" do
|
61
|
+
service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar'))
|
62
|
+
expect {
|
63
|
+
service.authenticate('foo', 'bar')
|
64
|
+
}.not_to raise_error
|
65
|
+
end
|
66
|
+
end
|
67
|
+
"""
|
68
|
+
|
69
|
+
Scenario: Passes when interfaces match
|
70
|
+
Then spec file with following content should pass:
|
71
|
+
"""ruby
|
72
|
+
describe AuthenticationService do
|
73
|
+
verify_contract(:authentication_service)
|
74
|
+
|
75
|
+
it "logs in the user" do
|
76
|
+
service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar'))
|
77
|
+
expect {
|
78
|
+
service.authenticate('foo', 'bar')
|
79
|
+
}.not_to raise_error
|
80
|
+
end
|
81
|
+
|
82
|
+
it "raises WrongPassword with incorrect credentials" do
|
83
|
+
service = AuthenticationService.new('foo' => Digest::SHA1.hexdigest('bar'))
|
84
|
+
expect {
|
85
|
+
service.authenticate('baz', 'bar')
|
86
|
+
}.to raise_error(WrongPassword)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
"""
|
90
|
+
|