matahari 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+