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