moqueue 0.1.4
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/CONTRIBUTORS.rdoc +3 -0
- data/README.rdoc +55 -0
- data/Rakefile +63 -0
- data/VERSION.yml +4 -0
- data/lib/moqueue.rb +15 -0
- data/lib/moqueue/fibers18.rb +57 -0
- data/lib/moqueue/matchers.rb +113 -0
- data/lib/moqueue/mock_broker.rb +53 -0
- data/lib/moqueue/mock_exchange.rb +147 -0
- data/lib/moqueue/mock_headers.rb +31 -0
- data/lib/moqueue/mock_queue.rb +138 -0
- data/lib/moqueue/object_methods.rb +41 -0
- data/lib/moqueue/overloads.rb +60 -0
- data/moqueue.gemspec +86 -0
- data/spec/examples/ack_spec.rb +93 -0
- data/spec/examples/basic_usage_spec.rb +101 -0
- data/spec/examples/example_helper.rb +16 -0
- data/spec/examples/logger_spec.rb +159 -0
- data/spec/examples/ping_pong_spec.rb +50 -0
- data/spec/examples/stocks_spec.rb +64 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/unit/matchers_spec.rb +102 -0
- data/spec/unit/mock_broker_spec.rb +40 -0
- data/spec/unit/mock_exchange_spec.rb +168 -0
- data/spec/unit/mock_headers_spec.rb +22 -0
- data/spec/unit/mock_queue_spec.rb +151 -0
- data/spec/unit/moqueue_spec.rb +5 -0
- data/spec/unit/object_methods_spec.rb +49 -0
- data/spec/unit/overloads_spec.rb +56 -0
- metadata +107 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "AMQP", "when mocked out by Moqueue" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
reset_broker
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have direct exchanges" do
|
10
|
+
queue = mock_queue("direct-exchanges")
|
11
|
+
queue.publish("you are correct, sir!")
|
12
|
+
queue.subscribe { |message| "do something with message" }
|
13
|
+
queue.received_message?("you are correct, sir!").should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have direct exchanges with acks" do
|
17
|
+
queue = mock_queue("direct-with-acks")
|
18
|
+
queue.publish("yessir!")
|
19
|
+
queue.subscribe(:ack => true) { |headers, message| headers.ack }
|
20
|
+
queue.received_ack_for_message?("yessir!").should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should have topic exchanges" do
|
24
|
+
topic = mock_exchange(:topic => "TATFT")
|
25
|
+
queue = mock_queue("rspec-fiend")
|
26
|
+
queue.bind(topic, :key => "bdd.*").subscribe { |msg| "do something" }
|
27
|
+
topic.publish("TATFT FTW", :key=> "bdd.4life")
|
28
|
+
queue.received_message?("TATFT FTW").should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have topic exchanges with acks" do
|
32
|
+
topic = mock_exchange(:topic => "animals")
|
33
|
+
queue = mock_queue("cat-lover")
|
34
|
+
queue.bind(topic, :key => "cats.#").subscribe(:ack => true) do |header, msg|
|
35
|
+
header.ack
|
36
|
+
"do something with message"
|
37
|
+
end
|
38
|
+
topic.publish("OMG kittehs!", :key => "cats.lolz.kittehs")
|
39
|
+
topic.received_ack_for_message?("OMG kittehs!").should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have fanout exchanges with acks" do
|
43
|
+
film = mock_exchange(:fanout => "Godfather")
|
44
|
+
one_actor = mock_queue("Jack Woltz")
|
45
|
+
other_actor = mock_queue("Captain McCluskey")
|
46
|
+
one_actor.bind(film).subscribe(:ack =>true) { |h,msg| h.ack && "horse head" }
|
47
|
+
other_actor.bind(film).subscribe(:ack => true) { |h,msg| h.ack && "dirty cops" }
|
48
|
+
offer = "you can't refuse"
|
49
|
+
film.publish(offer)
|
50
|
+
one_actor.received_message?(offer).should be_true
|
51
|
+
other_actor.received_message?(offer).should be_true
|
52
|
+
film.should have(2).acked_messages
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Moqueue, "with syntax sugar" do
|
58
|
+
|
59
|
+
before(:each) do
|
60
|
+
reset_broker
|
61
|
+
end
|
62
|
+
|
63
|
+
it "counts received messages" do
|
64
|
+
queue = mock_queue
|
65
|
+
queue.subscribe { |msg| msg.should_not be_nil }
|
66
|
+
5.times {queue.publish("no moar beers kthxbye")}
|
67
|
+
queue.should have(5).received_messages
|
68
|
+
end
|
69
|
+
|
70
|
+
it "counts acked messages" do
|
71
|
+
queue = mock_queue
|
72
|
+
queue.subscribe(:ack=>true) { |headers,msg| headers.ack }
|
73
|
+
5.times { queue.publish("time becomes a loop") }
|
74
|
+
queue.should have(5).acked_messages
|
75
|
+
end
|
76
|
+
|
77
|
+
it "makes the callback (#subscribe) block testable" do
|
78
|
+
emphasis = mock_queue
|
79
|
+
emphasis.subscribe { |msg| @emphasized = "**" + msg + "**" }
|
80
|
+
emphasis.run_callback("show emphasis").should == "**show emphasis**"
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
describe Moqueue, "when using custom rspec matchers" do
|
86
|
+
|
87
|
+
it "should accept syntax like queue.should have_received('a message')" do
|
88
|
+
queue = mock_queue("sugary")
|
89
|
+
queue.subscribe { |msg| "eat the message" }
|
90
|
+
queue.publish("a message")
|
91
|
+
queue.should have_received("a message")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should accept syntax like queue_or_exchange.should have_ack_for('a message')" do
|
95
|
+
queue = mock_queue("more sugar")
|
96
|
+
queue.subscribe(:ack => true) { |headers, msg| headers.ack }
|
97
|
+
queue.publish("another message")
|
98
|
+
queue.should have_ack_for("another message")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Moqueue, "when running the logger example" do
|
4
|
+
|
5
|
+
class MyLoggerRulez
|
6
|
+
def initialize *args, &block
|
7
|
+
opts = args.pop if args.last.is_a? Hash
|
8
|
+
opts ||= {}
|
9
|
+
|
10
|
+
printer(block) if block
|
11
|
+
|
12
|
+
@prop = opts
|
13
|
+
@tags = ([:timestamp] + args).uniq
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :prop
|
17
|
+
alias :base :prop
|
18
|
+
|
19
|
+
def log severity, *args
|
20
|
+
opts = args.pop if args.last.is_a? Hash and args.size != 1
|
21
|
+
opts ||= {}
|
22
|
+
opts = @prop.clone.update(opts)
|
23
|
+
|
24
|
+
data = args.shift
|
25
|
+
|
26
|
+
data = {:type => :exception,
|
27
|
+
:name => data.class.to_s.intern,
|
28
|
+
:backtrace => data.backtrace,
|
29
|
+
:message => data.message} if data.is_a? Exception
|
30
|
+
|
31
|
+
(@tags + args).each do |tag|
|
32
|
+
tag = tag.to_sym
|
33
|
+
case tag
|
34
|
+
when :timestamp
|
35
|
+
opts.update :timestamp => Time.now
|
36
|
+
when :hostname
|
37
|
+
@hostname ||= { :hostname => `hostname`.strip }
|
38
|
+
opts.update @hostname
|
39
|
+
when :process
|
40
|
+
@process_id ||= { :process_id => Process.pid,
|
41
|
+
:process_name => $0,
|
42
|
+
:process_parent_id => Process.ppid,
|
43
|
+
:thread_id => Thread.current.object_id }
|
44
|
+
opts.update :process => @process_id
|
45
|
+
else
|
46
|
+
(opts[:tags] ||= []) << tag
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.update(:severity => severity,
|
51
|
+
:msg => data)
|
52
|
+
|
53
|
+
print(opts)
|
54
|
+
unless MyLoggerRulez.disabled?
|
55
|
+
MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
|
56
|
+
end
|
57
|
+
|
58
|
+
opts
|
59
|
+
end
|
60
|
+
alias :method_missing :log
|
61
|
+
|
62
|
+
def print data = nil, &block
|
63
|
+
if block
|
64
|
+
@printer = block
|
65
|
+
elsif data.is_a? Proc
|
66
|
+
@printer = data
|
67
|
+
elsif data
|
68
|
+
(pr = @printer || self.class.printer) and pr.call(data)
|
69
|
+
else
|
70
|
+
@printer
|
71
|
+
end
|
72
|
+
end
|
73
|
+
alias :printer :print
|
74
|
+
|
75
|
+
def self.printer &block
|
76
|
+
@printer = block if block
|
77
|
+
@printer
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.disabled?
|
81
|
+
!!@disabled
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.enable
|
85
|
+
@disabled = false
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.disable
|
89
|
+
@disabled = true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
before(:all) do
|
95
|
+
overload_amqp
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def run_client
|
100
|
+
AMQP.start do
|
101
|
+
log = MyLoggerRulez.new
|
102
|
+
log.debug 'its working!'
|
103
|
+
|
104
|
+
log = MyLoggerRulez.new do |msg|
|
105
|
+
#require 'pp'
|
106
|
+
#pp msg
|
107
|
+
#puts
|
108
|
+
end
|
109
|
+
|
110
|
+
log.info '123'
|
111
|
+
log.debug [1,2,3]
|
112
|
+
log.debug :one => 1, :two => 2
|
113
|
+
log.error Exception.new('123')
|
114
|
+
|
115
|
+
log.info '123', :process_id => Process.pid
|
116
|
+
log.info '123', :process
|
117
|
+
log.debug 'login', :session => 'abc', :user => 123
|
118
|
+
|
119
|
+
log = MyLoggerRulez.new(:webserver, :timestamp, :hostname, &log.printer)
|
120
|
+
log.info 'Request for /', :GET, :session => 'abc'
|
121
|
+
|
122
|
+
#AMQP.stop{ EM.stop }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def run_server
|
127
|
+
AMQP.start(:host => 'localhost') do
|
128
|
+
|
129
|
+
@server_queue = MQ.queue('logger')
|
130
|
+
@server_queue.bind(MQ.fanout('logging', :durable => true)).subscribe do |msg|
|
131
|
+
msg = Marshal.load(msg)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should get the expected results" do
|
137
|
+
EM.run do
|
138
|
+
threads = []
|
139
|
+
threads << Thread.new do
|
140
|
+
run_server
|
141
|
+
end
|
142
|
+
threads << Thread.new do
|
143
|
+
run_client
|
144
|
+
end
|
145
|
+
|
146
|
+
EM.add_timer(0.1) do
|
147
|
+
@server_queue.should have(9).received_messages
|
148
|
+
webserver_log = Marshal.load(@server_queue.received_messages.last)
|
149
|
+
webserver_log[:tags].should == [:webserver, :GET]
|
150
|
+
webserver_log[:msg].should == "Request for /"
|
151
|
+
|
152
|
+
EM.stop
|
153
|
+
threads.each { |t| t.join }
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/example_helper'
|
3
|
+
|
4
|
+
describe Moqueue, "when testing the ping pong example" do
|
5
|
+
include ExampleHelper
|
6
|
+
|
7
|
+
def ping_pong
|
8
|
+
AMQP.start(:host => 'localhost') do
|
9
|
+
|
10
|
+
# AMQP.logging = true
|
11
|
+
|
12
|
+
amq = MQ.new
|
13
|
+
EM.add_periodic_timer(0.1){
|
14
|
+
@counter_val = counter
|
15
|
+
capture_output @counter_val, :sending, 'ping'
|
16
|
+
amq.queue('one').publish('ping')
|
17
|
+
}
|
18
|
+
|
19
|
+
amq = MQ.new
|
20
|
+
amq.queue('one').subscribe{ |msg|
|
21
|
+
capture_output @counter_val, 'one', :received, msg, :sending, 'pong'
|
22
|
+
amq.queue('two').publish('pong')
|
23
|
+
}
|
24
|
+
|
25
|
+
amq = MQ.new
|
26
|
+
amq.queue('two').subscribe{ |msg|
|
27
|
+
capture_output @counter_val, 'two', :received, msg
|
28
|
+
}
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
before(:all) do
|
35
|
+
overload_amqp
|
36
|
+
end
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
reset!
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should get the correct result without error" do
|
43
|
+
Timeout::timeout(5) do
|
44
|
+
ping_pong
|
45
|
+
end
|
46
|
+
expected = [[1, :sending, "ping"], [1, "one", :received, "ping", :sending, "pong"], [1, "two", :received, "pong"],
|
47
|
+
[2, :sending, "ping"], [2, "one", :received, "ping", :sending, "pong"], [2, "two", :received, "pong"]]
|
48
|
+
@captured_output.should == expected
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/example_helper'
|
3
|
+
|
4
|
+
describe Moqueue, "when running the stocks example" do
|
5
|
+
include ExampleHelper
|
6
|
+
|
7
|
+
def run_stocks
|
8
|
+
AMQP.start(:host => 'localhost') do
|
9
|
+
|
10
|
+
def log *args
|
11
|
+
#p [ Time.now, *args ]
|
12
|
+
end
|
13
|
+
|
14
|
+
def publish_stock_prices
|
15
|
+
mq = MQ.new
|
16
|
+
counter = 0
|
17
|
+
EM.add_periodic_timer(0.1){
|
18
|
+
counter += 1
|
19
|
+
EM.stop if counter > 5
|
20
|
+
|
21
|
+
{:appl => 170+rand(1000)/100.0, :msft => 22+rand(500)/100.0}.each do |stock, price|
|
22
|
+
stock = "usd.#{stock}"
|
23
|
+
|
24
|
+
log :publishing, stock, price
|
25
|
+
mq.topic('stocks').publish(price, :key => stock)
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def watch_appl_stock
|
31
|
+
mq = MQ.new
|
32
|
+
@apple_queue = mq.queue('apple stock')
|
33
|
+
@apple_queue.bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
|
34
|
+
log 'apple stock', price
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def watch_us_stocks
|
39
|
+
mq = MQ.new
|
40
|
+
@us_stocks = mq.queue('us stocks')
|
41
|
+
@us_stocks.bind(mq.topic('stocks'), :key => 'usd.*').subscribe{ |info, price|
|
42
|
+
log 'us stock', info.routing_key, price
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
publish_stock_prices
|
47
|
+
watch_appl_stock
|
48
|
+
watch_us_stocks
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
before(:each) do
|
54
|
+
overload_amqp
|
55
|
+
reset_broker
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should get the correct results" do
|
59
|
+
run_stocks
|
60
|
+
@us_stocks.should have(12).received_messages
|
61
|
+
@apple_queue.should have(6).received_messages
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-c -f specdoc
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "spec"
|
3
|
+
|
4
|
+
Spec::Runner.configure do |config|
|
5
|
+
config.mock_with :mocha
|
6
|
+
end
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + "/../lib/moqueue"
|
9
|
+
|
10
|
+
# Make sure tests fail if deferred blocks (for susbscribe and pop) don't get called
|
11
|
+
def ensure_deferred_block_called(opts={:times=>1})
|
12
|
+
@poke_me = mock("poke_me")
|
13
|
+
@poke_me.expects(:deferred_block_called).times(opts[:times])
|
14
|
+
end
|
15
|
+
|
16
|
+
def deferred_block_called
|
17
|
+
@poke_me.deferred_block_called
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def ensure_deferred_block_skipped
|
22
|
+
@skip_me = mock("poke_me")
|
23
|
+
@skip_me.expects(:deferred_block_called).times(0)
|
24
|
+
end
|
25
|
+
|
26
|
+
include Moqueue
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Matchers do
|
4
|
+
class MatcherHarness
|
5
|
+
include Moqueue::Matchers
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@matchable = MatcherHarness.new
|
10
|
+
@mock_moqueue = mock("mock Moqueue::MockQueue")
|
11
|
+
@failure_exception = Spec::Expectations::ExpectationNotMetError
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should include matchers in describe blocks automatically when using rspec" do
|
15
|
+
self.class.include?(Moqueue::Matchers).should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should implement Object#should have_received_message" do
|
19
|
+
@mock_moqueue.expects(:received_message?).with("matchtacular").returns(true)
|
20
|
+
@mock_moqueue.should have_received_message("matchtacular")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should implement Object#should_not have_received_message" do
|
24
|
+
@mock_moqueue.expects(:received_message?).with("no match").returns(false)
|
25
|
+
@mock_moqueue.should_not have_received_message("no match")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should implement Object#should have_exact_routing_key(key)" do
|
29
|
+
@mock_moqueue.expects(:received_routing_key?).with("routing.key").returns(true)
|
30
|
+
@mock_moqueue.should have_received_exact_routing_key("routing.key")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should implement Object#should_not have_exact_routing_key(key)" do
|
34
|
+
@mock_moqueue.expects(:received_routing_key?).with("routing.key").returns(false)
|
35
|
+
@mock_moqueue.should_not have_received_exact_routing_key("routing.key")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should have a useful failure message" do
|
39
|
+
@mock_moqueue.expects(:received_message?).with("this fails").returns(false)
|
40
|
+
failing_example = lambda {@mock_moqueue.should have_received_message("this fails")}
|
41
|
+
error_message = "expected #{@mock_moqueue.inspect} to have received message ``this fails''"
|
42
|
+
failing_example.should raise_error(@failure_exception, error_message)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should have a useful negative failure message" do
|
46
|
+
@mock_moqueue.expects(:received_message?).with("FAIL").returns(true)
|
47
|
+
failing_example = lambda{@mock_moqueue.should_not have_received_message("FAIL")}
|
48
|
+
error_message = "expected #{@mock_moqueue.inspect} to not have received message ``FAIL''"
|
49
|
+
failing_example.should raise_error(@failure_exception, error_message)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should fail gracefully if object being tested for #have_received doesn't respond to #received_message?" do
|
53
|
+
begin
|
54
|
+
Object.new.should have_received_message("foo")
|
55
|
+
rescue => e
|
56
|
+
end
|
57
|
+
e.should be_a(NoMethodError)
|
58
|
+
e.message.should match(/you can't use \`\`should have_received_message\'\' on #\<Object/)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should alias #have_received_message as #have_received for less verbosity" do
|
62
|
+
@matchable.should respond_to(:have_received)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should alias #have_received_ack_for as #have_ack_for for less verbosity" do
|
66
|
+
@matchable.should respond_to(:have_ack_for)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should implement Object#should have_received_ack_for(msg_text)" do
|
70
|
+
@mock_moqueue.expects(:received_ack_for_message?).with("foo bar").returns(true)
|
71
|
+
@mock_moqueue.should have_received_ack_for("foo bar")
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should implement Object#should_not have_received_ack_for(msg_text)" do
|
75
|
+
@mock_moqueue.expects(:received_ack_for_message?).with("bar baz").returns(false)
|
76
|
+
@mock_moqueue.should_not have_received_ack_for("bar baz")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should have a helpful failure message" do
|
80
|
+
@mock_moqueue.expects(:received_ack_for_message?).with("foo baz").returns(false)
|
81
|
+
failure = lambda {@mock_moqueue.should have_received_ack_for("foo baz")}
|
82
|
+
fail_msg = "expected #{@mock_moqueue.inspect} to have received an ack for the message ``foo baz''"
|
83
|
+
failure.should raise_error(@failure_exception, fail_msg)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should have a helpful negative failure message" do
|
87
|
+
@mock_moqueue.expects(:received_ack_for_message?).with("bar foo").returns(true)
|
88
|
+
failure = lambda {@mock_moqueue.should_not have_received_ack_for("bar foo")}
|
89
|
+
fail_msg = "expected #{@mock_moqueue.inspect} to not have received an ack for the message ``bar foo''"
|
90
|
+
failure.should raise_error(@failure_exception, fail_msg)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should fail gracefully if object being tested for #have_received_ack_for doesn't respond to #received_ack_for_message?" do
|
94
|
+
begin
|
95
|
+
Object.new.should have_received_message("foo")
|
96
|
+
rescue => e
|
97
|
+
end
|
98
|
+
e.should be_a(NoMethodError)
|
99
|
+
e.message.should match(/you can't use \`\`should have_received_message\'\' on #\<Object/)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|