matahari 0.1.1

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.
data/README.rdoc ADDED
@@ -0,0 +1,67 @@
1
+ = matahari
2
+
3
+ * https://github.com/mortice/matahari
4
+
5
+ == DESCRIPTION:
6
+
7
+ Matahari is a test spy library, heavily inspired by Java's Mockito and btakita's RR.
8
+
9
+ It is designed to allow you to test interactions between objects *without* requiring
10
+ you to specify all method calls, including uninteresting ones.
11
+
12
+ == SYNOPSIS:
13
+
14
+ require 'matahari'
15
+
16
+ describe "my object" do
17
+ obj = MyAwesomeObject.new
18
+ collaborator = spy(:collaborator)
19
+
20
+ obj.my_awesome_method
21
+
22
+ collaborator.should have_received(3.times).some_interesting_method("Some", "arguments")
23
+ end
24
+
25
+ See also the cucumber feature, easily viewable at http://relishapp.com/Mortice/matahari
26
+
27
+ == KNOWN ISSUES/TODO:
28
+
29
+ * Stubbing implementation is incredibly basic
30
+ * Not very usable at all for test/unit users (but this is easily fixed with an assert_received method)
31
+ * No support for argument matchers - arguments must match exactly via == or not at all
32
+
33
+ == INSTALL:
34
+
35
+ * gem install matahari
36
+
37
+ == ACKNOWLEDGEMENTS:
38
+
39
+ * David Chelimsky, for putting up with my 'robust' criticisms of RSpec mocks
40
+ * Aslak Hellesøy, for giving interesting feedback on an early version
41
+ * Szczepan Faber, for creating Mockito
42
+ * Brian Takita, for creating RR
43
+
44
+ == LICENSE:
45
+
46
+ (The MIT License)
47
+
48
+ Copyright (c) 2010-2011 Tom Stuart
49
+
50
+ Permission is hereby granted, free of charge, to any person obtaining
51
+ a copy of this software and associated documentation files (the
52
+ 'Software'), to deal in the Software without restriction, including
53
+ without limitation the rights to use, copy, modify, merge, publish,
54
+ distribute, sublicense, and/or sell copies of the Software, and to
55
+ permit persons to whom the Software is furnished to do so, subject to
56
+ the following conditions:
57
+
58
+ The above copyright notice and this permission notice shall be
59
+ included in all copies or substantial portions of the Software.
60
+
61
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
62
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
63
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
64
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
65
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
66
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
67
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/matahari.rb ADDED
@@ -0,0 +1,14 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Matahari
5
+ VERSION = '0.1.1'
6
+ end
7
+
8
+ require 'matahari/spy'
9
+ require 'matahari/debriefing'
10
+ require 'matahari/rspec/matchers'
11
+
12
+ def spy(name = nil)
13
+ Spy.new(name)
14
+ end
@@ -0,0 +1,62 @@
1
+ class Debriefing
2
+ def initialize(expected_calls = nil)
3
+ @expected_calls = expected_calls
4
+ end
5
+
6
+ def matches?(subject)
7
+ @subject = subject
8
+ invocations_matching_method = subject.invocations.select {|i| i[:method] == @call_to_verify}
9
+ method_matched = invocations_matching_method.size > 0
10
+ no_args = @args_to_verify.size == 0
11
+ @matching_calls = invocations_matching_method.select {|i| i[:args].flatten === @args_to_verify}.size
12
+ if @expected_calls
13
+ checked_number_of_calls = true
14
+ args_match = @matching_calls == @expected_calls
15
+ else
16
+ args_match = @matching_calls > 0
17
+ end
18
+ matching = if checked_number_of_calls
19
+ args_match
20
+ else
21
+ method_matched && no_args || args_match
22
+ end
23
+
24
+ if matching
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ def failure_message_for_should_not
32
+ "Spy(:#{@subject.name}) expected not to receive :#{@call_to_verify} but received it #{prettify_times(@matching_calls)}"
33
+ end
34
+
35
+ def failure_message_for_should
36
+ if @args_to_verify.size > 0
37
+ "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify}(#{@args_to_verify.map(&:inspect).join(", ")}) #{prettify_times(@expected_calls)}, received #{prettify_times(@matching_calls)}"
38
+ else
39
+ "Spy(:#{@subject.name}) expected to receive :#{@call_to_verify} #{prettify_times(@expected_calls)}, received #{prettify_times(@matching_calls)}"
40
+ end
41
+ end
42
+
43
+ def method_missing(sym, *args, &block)
44
+ @call_to_verify = sym
45
+ @args_to_verify = args
46
+ self
47
+ end
48
+
49
+ def prettify_times(times)
50
+ case times
51
+ when 1
52
+ "once"
53
+ when nil
54
+ "once"
55
+ when 2
56
+ "twice"
57
+ else
58
+ "#{times} times"
59
+ end
60
+ end
61
+ private :prettify_times
62
+ end
@@ -0,0 +1,10 @@
1
+ def have_received(times = nil)
2
+ if times
3
+ @calls_expected = 0
4
+ times.each { @calls_expected+= 1 }
5
+
6
+ Debriefing.new(@calls_expected)
7
+ else
8
+ Debriefing.new
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ class Spy
2
+ attr_reader :name, :invocations
3
+
4
+ def initialize(name = nil)
5
+ @name = name if name
6
+ @invocations = []
7
+ @stubbed_calls = {}
8
+ end
9
+
10
+ def stubs(sym, &block)
11
+ @stubbed_calls[sym] = block
12
+ end
13
+
14
+ def method_missing(sym, *args, &block)
15
+ if @verifying
16
+ raise
17
+ else
18
+ record_invocation(sym, args)
19
+ @stubbed_calls[sym].call if @stubbed_calls[sym]
20
+ end
21
+ end
22
+
23
+ def has_received?(times=nil)
24
+ if times
25
+ @calls_expected = 0
26
+ times.each { @calls_expected+= 1 }
27
+
28
+ Debriefing.new(@calls_expected)
29
+ else
30
+ Debriefing.new
31
+ end
32
+ end
33
+
34
+ private
35
+ def record_invocation(sym, *args)
36
+ @invocations << {:method => sym, :args => args}
37
+ end
38
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+ describe Debriefing do
3
+ it "matches simple invocations" do
4
+ #we have to use rspec mocks here because testing matahari with matahari
5
+ #makes my brain hurt
6
+
7
+ subject = mock(:subject)
8
+ debriefing = Debriefing.new
9
+
10
+ subject.should_receive(:invocations).and_return([{:method => :one, :args => [[]]}])
11
+
12
+ debriefing.one
13
+
14
+ debriefing.matches?(subject).should be_true
15
+ end
16
+
17
+ it "matches invocations based on arguments" do
18
+ subject = mock(:subject)
19
+ correct_debriefing = Debriefing.new
20
+ incorrect_debriefing = Debriefing.new
21
+
22
+ subject.should_receive(:invocations).twice.and_return([{:method => :one, :args => [["Hello", "goodbye"]]}])
23
+
24
+ correct_debriefing.one("Hello", "goodbye")
25
+ incorrect_debriefing.one("Hello", "goodbye", "Hello again")
26
+
27
+ correct_debriefing.matches?(subject).should be_true
28
+ incorrect_debriefing.matches?(subject).should be_false
29
+ end
30
+
31
+ it "gives a failure message for should when method not called" do
32
+ subject = mock(:subject)
33
+ debriefing = Debriefing.new
34
+
35
+ subject.should_receive(:invocations).and_return([{:method => :one, :args => [[]]}])
36
+ subject.should_receive(:name).and_return(:subject)
37
+
38
+ debriefing.two
39
+
40
+ debriefing.matches?(subject).should be_false
41
+
42
+ debriefing.failure_message_for_should.should == "Spy(:subject) expected to receive :two once, received 0 times"
43
+ end
44
+
45
+ it "gives a failure message for should when method called with wrong arguments" do
46
+ subject = mock(:subject)
47
+ debriefing = Debriefing.new
48
+
49
+ subject.should_receive(:invocations).and_return([{:method => :one, :args => [[]]}])
50
+ subject.should_receive(:name).and_return(:subject)
51
+
52
+ debriefing.one("Hello")
53
+
54
+ debriefing.matches?(subject).should be_false
55
+
56
+ debriefing.failure_message_for_should.should == "Spy(:subject) expected to receive :one(\"Hello\") once, received 0 times"
57
+ end
58
+
59
+ it "gives a failure message for should when method called wrong number of times" do
60
+ subject = mock(:subject)
61
+ debriefing = Debriefing.new(2)
62
+
63
+ subject.should_receive(:invocations).and_return([{:method => :one, :args => [[]]}])
64
+ subject.should_receive(:name).and_return(:subject)
65
+
66
+ debriefing.one
67
+
68
+ debriefing.matches?(subject).should be_false
69
+
70
+ debriefing.failure_message_for_should.should == "Spy(:subject) expected to receive :one twice, received once"
71
+ end
72
+
73
+ it "gives a failure message for should not" do
74
+ subject = mock(:subject)
75
+ debriefing = Debriefing.new
76
+
77
+ subject.should_receive(:invocations).and_return([{:method => :two, :args => [[]]}])
78
+ subject.should_receive(:name).and_return(:subject)
79
+
80
+ debriefing.two
81
+
82
+ debriefing.matches?(subject).should be_true
83
+
84
+ debriefing.failure_message_for_should_not.should == "Spy(:subject) expected not to receive :two but received it once"
85
+ end
86
+ end
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + "/../lib/matahari"
2
+ require 'rspec'
data/spec/spy_spec.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Spy" do
4
+ it "takes an optional name parameter" do
5
+ named = Spy.new(:bond)
6
+ unnamed = Spy.new
7
+
8
+ named.name.should == :bond
9
+ unnamed.name.should == nil
10
+ end
11
+
12
+ context "in the field" do
13
+ it "remains as a sleeper agent until called upon" do
14
+ mata_hari = Spy.new(:mata_hari)
15
+ lambda do
16
+ mata_hari.one
17
+ mata_hari.two
18
+ mata_hari.three
19
+ end.should_not raise_error NoMethodError
20
+ end
21
+
22
+ it "captures communications" do
23
+ mata_hari = Spy.new(:mata_hari)
24
+
25
+ mata_hari.one
26
+ mata_hari.two
27
+
28
+ mata_hari.invocations.should == [{:method => :one, :args => [[]]}, {:method => :two, :args => [[]]}]
29
+ end
30
+
31
+ it "captures the details of communications" do
32
+ mata_hari = Spy.new(:mata_hari)
33
+
34
+ mata_hari.one
35
+ mata_hari.two("Hello")
36
+
37
+ mata_hari.invocations.should == [{:method => :one, :args => [[]]}, {:method => :two, :args => [["Hello"]]}]
38
+ end
39
+
40
+ it "doesn't stop you stubbing" do
41
+ mata_hari = Spy.new(:mata_hari)
42
+
43
+ mata_hari.stubs(:test) { "Hello" }
44
+
45
+ mata_hari.test.should == "Hello"
46
+ mata_hari.invocations.should == [{:method => :test, :args => [[]]}]
47
+ end
48
+ end
49
+ end
data/test/spy_test.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'matahari'
3
+
4
+ class SpyTest < Test::Unit::TestCase
5
+ #minimal test to make sure we stay compatible. For full tests, see spec/
6
+ def test_matahari_works_with_test_unit
7
+ mata_hari = spy(:mata_hari)
8
+
9
+ mata_hari.one
10
+
11
+ #I know this syntax sucks but at least it shows you don't *need* rspec to use matahari.
12
+ #TODO make an assert_received method for test/unit
13
+ assert mata_hari.has_received?.one.matches?(mata_hari)
14
+ assert !mata_hari.has_received?.two.matches?(mata_hari)
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: matahari
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Tom Stuart
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-13 00:00:00 +00:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: cucumber
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :development
45
+ version_requirements: *id002
46
+ description:
47
+ email: tom@therye.org
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ extra_rdoc_files:
53
+ - README.rdoc
54
+ files:
55
+ - README.rdoc
56
+ - test/spy_test.rb
57
+ - spec/debriefing_spec.rb
58
+ - spec/spec_helper.rb
59
+ - spec/spy_spec.rb
60
+ - lib/matahari/debriefing.rb
61
+ - lib/matahari/rspec/matchers.rb
62
+ - lib/matahari/spy.rb
63
+ - lib/matahari.rb
64
+ has_rdoc: true
65
+ homepage: https://github.com/mortice/matahari
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --main
71
+ - README.rdoc
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.7
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Test spy library, inspired by Mockito and RR
97
+ test_files: []
98
+