rspec-process-mocks 0.0.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/.gitignore +11 -0
- data/Gemfile +40 -0
- data/README.md +54 -0
- data/cucumber.yml +2 -0
- data/features/message_expectations/expect_message.feature +154 -0
- data/features/support/env.rb +9 -0
- data/lib/rspec/process_mocks/framework.rb +4 -0
- data/lib/rspec/process_mocks/message_expectation.rb +66 -0
- data/lib/rspec/process_mocks/method_double.rb +28 -0
- data/lib/rspec/process_mocks/methods.rb +17 -0
- data/lib/rspec/process_mocks/proxy.rb +17 -0
- data/lib/rspec/process_mocks/version.rb +7 -0
- data/lib/rspec/process_mocks.rb +7 -0
- data/rspec-process-mocks.gemspec +22 -0
- metadata +69 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
### rspec libs
|
4
|
+
%w[rspec rspec-core rspec-expectations rspec-mocks].each do |lib|
|
5
|
+
library_path = File.expand_path("../../#{lib}", __FILE__)
|
6
|
+
if File.exist?(library_path)
|
7
|
+
gem lib, :path => library_path
|
8
|
+
else
|
9
|
+
gem lib, :git => "git://github.com/rspec/#{lib}.git"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
### dev dependencies
|
14
|
+
gem "rake", "~> 0.8"
|
15
|
+
gem "cucumber", "~> 0.10.2"
|
16
|
+
gem "aruba", "~> 0.3.6"
|
17
|
+
gem "rcov", "0.9.9", :platforms => :mri
|
18
|
+
gem "relish", "0.2.0"
|
19
|
+
gem "guard-rspec", "0.1.9"
|
20
|
+
gem "growl", "1.0.3"
|
21
|
+
gem "nokogiri", "1.4.4"
|
22
|
+
|
23
|
+
platforms :mri_18 do
|
24
|
+
gem 'ruby-debug'
|
25
|
+
end
|
26
|
+
|
27
|
+
platforms :mri_19 do
|
28
|
+
gem 'linecache19', '0.5.11' # 0.5.12 cannot install on 1.9.1, and 0.5.11 appears to work with both 1.9.1 & 1.9.2
|
29
|
+
gem 'ruby-debug19'
|
30
|
+
gem 'ruby-debug-base19', RUBY_VERSION == '1.9.1' ? '0.11.23' : '~> 0.11.24'
|
31
|
+
end
|
32
|
+
|
33
|
+
platforms :mri_18, :mri_19 do
|
34
|
+
gem "rb-fsevent", "~> 0.3.9"
|
35
|
+
gem "ruby-prof", "~> 0.9.2"
|
36
|
+
end
|
37
|
+
|
38
|
+
platforms :jruby do
|
39
|
+
gem "jruby-openssl"
|
40
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
WARNING: This is just a big hack right now. If you are interested in using this
|
2
|
+
project but I haven't removed this warning yet, please bug me to lessen the
|
3
|
+
hackiness and make it more usable.
|
4
|
+
|
5
|
+
|
6
|
+
# RSpec Process Mocks
|
7
|
+
|
8
|
+
rspec-process-mocks is an addon to rspec-mocks that provides support
|
9
|
+
for method stubs, fakes, and message expectations within child processes.
|
10
|
+
|
11
|
+
## Documentation
|
12
|
+
|
13
|
+
## Setup
|
14
|
+
|
15
|
+
Gemfile
|
16
|
+
|
17
|
+
gem 'rspec-process-mocks', :git => 'git://github.com/thoughtless/rspec-process-mocks.git'
|
18
|
+
|
19
|
+
`bundle install`
|
20
|
+
|
21
|
+
spec/spec_helper.rb
|
22
|
+
|
23
|
+
require 'rspec/process_mocks' # This line must be after 'config.mock_with :rspec'
|
24
|
+
|
25
|
+
## Message Expectations
|
26
|
+
|
27
|
+
describe "some action" do
|
28
|
+
context "when bad stuff happens" do
|
29
|
+
it "logs the error" do
|
30
|
+
logger = double('logger')
|
31
|
+
doer = Doer.new(logger)
|
32
|
+
logger.should_receive_in_child_process(:log)
|
33
|
+
doer.do_something_with(:bad_data)
|
34
|
+
sleep 0.1 # Leave time for the child process to run.
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Doer
|
40
|
+
attr_accessor :logger
|
41
|
+
def do_something_with(data)
|
42
|
+
Process.fork { logger.log(data) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
## Contribute
|
47
|
+
|
48
|
+
See [http://github.com/thoughtless/rspec-process-mocks](http://github.com/thoughtless/rspec-process-mocks)
|
49
|
+
|
50
|
+
## Also see
|
51
|
+
|
52
|
+
* [http://github.com/rspec/rspec](http://github.com/rspec/rspec)
|
53
|
+
* [http://github.com/rspec/rspec-core](http://github.com/rspec/rspec-core)
|
54
|
+
* [http://github.com/rspec/rspec-expectations](http://github.com/rspec/rspec-expectations)
|
data/cucumber.yml
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
Feature: expect a message
|
2
|
+
|
3
|
+
Use should_receive() to set an expectation that a receiver should receive a
|
4
|
+
message before the example is completed.
|
5
|
+
|
6
|
+
Scenario: expect a message
|
7
|
+
Given a file named "spec/account_spec.rb" with:
|
8
|
+
"""
|
9
|
+
RSpec.configuration.mock_framework = :rspec
|
10
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
11
|
+
require 'rspec/process_mocks'
|
12
|
+
require "account"
|
13
|
+
|
14
|
+
describe Account do
|
15
|
+
context "when closed" do
|
16
|
+
it "logs an account closed message in a child process" do
|
17
|
+
|
18
|
+
logger = double("logger")
|
19
|
+
account = Account.new
|
20
|
+
account.logger = logger
|
21
|
+
|
22
|
+
logger.should_receive_in_child_process(:account_closed)
|
23
|
+
|
24
|
+
account.close
|
25
|
+
|
26
|
+
sleep 0.1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
"""
|
31
|
+
And a file named "lib/account.rb" with:
|
32
|
+
"""
|
33
|
+
class Account
|
34
|
+
attr_accessor :logger
|
35
|
+
|
36
|
+
def close
|
37
|
+
Process.fork { logger.account_closed }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
"""
|
41
|
+
When I run `rspec spec/account_spec.rb`
|
42
|
+
Then the output should contain "1 example, 0 failures"
|
43
|
+
|
44
|
+
Scenario: expect a message, but it isn't called
|
45
|
+
Given a file named "spec/account_spec.rb" with:
|
46
|
+
"""
|
47
|
+
RSpec.configuration.mock_framework = :rspec
|
48
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
49
|
+
require 'rspec/process_mocks'
|
50
|
+
require "account"
|
51
|
+
|
52
|
+
describe Account do
|
53
|
+
context "when closed" do
|
54
|
+
it "logs an account closed message in a child process" do
|
55
|
+
|
56
|
+
logger = double("logger")
|
57
|
+
account = Account.new
|
58
|
+
account.logger = logger
|
59
|
+
|
60
|
+
logger.should_receive_in_child_process(:account_closed)
|
61
|
+
|
62
|
+
account.close
|
63
|
+
|
64
|
+
sleep 0.1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
"""
|
69
|
+
And a file named "lib/account.rb" with:
|
70
|
+
"""
|
71
|
+
class Account
|
72
|
+
attr_accessor :logger
|
73
|
+
|
74
|
+
def close
|
75
|
+
Process.fork { logger.object_id }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
"""
|
79
|
+
When I run `rspec spec/account_spec.rb`
|
80
|
+
Then the output should contain "1 example, 1 failure"
|
81
|
+
And the output should contain "expected: 1 time"
|
82
|
+
And the output should contain "received: 0 times"
|
83
|
+
|
84
|
+
@wip
|
85
|
+
Scenario: expect a message with an argument
|
86
|
+
Given a file named "spec/account_spec.rb" with:
|
87
|
+
"""
|
88
|
+
RSpec.configuration.mock_framework = :rspec
|
89
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
90
|
+
require 'rspec/process_mocks'
|
91
|
+
require "account"
|
92
|
+
|
93
|
+
describe Account do
|
94
|
+
context "when closed" do
|
95
|
+
it "logs an account closed message" do
|
96
|
+
logger = double("logger")
|
97
|
+
account = Account.new
|
98
|
+
account.logger = logger
|
99
|
+
|
100
|
+
logger.should_receive_in_child_process(:account_closed).with(account)
|
101
|
+
|
102
|
+
account.close
|
103
|
+
|
104
|
+
puts 'start sleep'
|
105
|
+
sleep 1
|
106
|
+
puts 'end sleep'
|
107
|
+
|
108
|
+
account.logger
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
"""
|
113
|
+
And a file named "lib/account.rb" with:
|
114
|
+
"""
|
115
|
+
class Account
|
116
|
+
attr_accessor :logger
|
117
|
+
|
118
|
+
def close
|
119
|
+
Process.fork { logger.account_closed(self) }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
"""
|
123
|
+
When I run `rspec spec/account_spec.rb`
|
124
|
+
Then the output should contain "1 example, 0 failures"
|
125
|
+
|
126
|
+
@wip
|
127
|
+
Scenario: provide a return value
|
128
|
+
Given a file named "message_expectation_spec.rb" with:
|
129
|
+
"""
|
130
|
+
RSpec.configuration.mock_framework = :rspec
|
131
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'lib'))
|
132
|
+
require 'rspec/process_mocks'
|
133
|
+
describe "a message expectation" do
|
134
|
+
context "with a return value" do
|
135
|
+
context "specified in a block" do
|
136
|
+
it "returns the specified value" do
|
137
|
+
receiver = double("receiver")
|
138
|
+
receiver.should_receive_in_child_process(:message) { :return_value }
|
139
|
+
receiver.message.should eq(:return_value)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "specified with and_return" do
|
144
|
+
it "returns the specified value" do
|
145
|
+
receiver = double("receiver")
|
146
|
+
receiver.should_receive_in_child_process(:message).and_return(:return_value)
|
147
|
+
receiver.message.should eq(:return_value)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
"""
|
153
|
+
When I run `rspec message_expectation_spec.rb`
|
154
|
+
Then the output should contain "2 examples, 0 failures"
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module ProcessMocks
|
5
|
+
class ChildProcessMessageExpectation < RSpec::Mocks::MessageExpectation
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
|
9
|
+
# The tempfile is used for interprocess communication
|
10
|
+
@tempfile = Tempfile.new("should-receive-#{@expected_from.object_id}-#{@sym}")
|
11
|
+
end
|
12
|
+
def verify_messages_received
|
13
|
+
# Maybe we should add a sleep loop here if @tempfile is empty. This may
|
14
|
+
# prevent a user of this library from having to add a sleep in the spec.
|
15
|
+
# Maybe this:
|
16
|
+
# @tempfile.rewind
|
17
|
+
# lines = @tempfile.readlines
|
18
|
+
# timeout = Configure.verify_timeout
|
19
|
+
# while lines.empty? && timeout > 0
|
20
|
+
# sleep 0.1
|
21
|
+
# timeout -= 0.1
|
22
|
+
# @tempfile.rewind
|
23
|
+
# lines = @tempfile.readlines
|
24
|
+
# end
|
25
|
+
|
26
|
+
@tempfile.rewind
|
27
|
+
lines = @tempfile.readlines
|
28
|
+
#puts "lines in verify_messages_received: #{lines.inspect}"
|
29
|
+
return true if lines.size == 1 #&& # Called exactly once.
|
30
|
+
# lines[0] == "[]\n" # Called with no arguments.
|
31
|
+
|
32
|
+
generate_error
|
33
|
+
rescue RSpec::Mocks::MockExpectationError => error
|
34
|
+
error.backtrace.insert(0, @expected_from)
|
35
|
+
Kernel::raise error
|
36
|
+
end
|
37
|
+
|
38
|
+
# This is a hack to get it to work. But it means we can only ever expect
|
39
|
+
# exactly 1 message.
|
40
|
+
def matches_exact_count?
|
41
|
+
lines.size == 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# This method is executed in the _child_ process when the stubbed method
|
45
|
+
# is called.
|
46
|
+
def invoke(*args, &block)
|
47
|
+
|
48
|
+
msg = args.inspect
|
49
|
+
@tempfile.puts msg
|
50
|
+
@tempfile.flush
|
51
|
+
# puts "##{@sym} called on #{@expected_from.class}-#{@expected_from.object_id}; args: #{args.inspect}"
|
52
|
+
# yield block, *args if defined?(block)
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is a hack to get it to work. But it means we don't get info about
|
56
|
+
# "similar" methods in the failure output. Similar messages are those with
|
57
|
+
# a matching method name, but not matching arguments.
|
58
|
+
# Note: this is only a rough definition of "similar" methods. "Similar"
|
59
|
+
# methods are not part of RSpec's external API, so their definition is not
|
60
|
+
# strictly documented.
|
61
|
+
def similar_messages
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RSpec
|
2
|
+
module ProcessMocks
|
3
|
+
module MethodDouble
|
4
|
+
# This has no checking for existing expectations on this object (like RSpec
|
5
|
+
# does). It will probably only work if you set one expectation per object.
|
6
|
+
def add_child_process_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
|
7
|
+
configure_method
|
8
|
+
# expectation = if existing_stub = stubs.first
|
9
|
+
# existing_stub.build_child(expected_from, block, 1, opts)
|
10
|
+
# else
|
11
|
+
# ChildProcessMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
|
12
|
+
# end
|
13
|
+
# expectations << expectation
|
14
|
+
expectation = ChildProcessMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
|
15
|
+
expectations << expectation
|
16
|
+
expectation
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module RSpec
|
23
|
+
module Mocks
|
24
|
+
class MethodDouble
|
25
|
+
include RSpec::ProcessMocks::MethodDouble
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RSpec
|
2
|
+
module ProcessMocks
|
3
|
+
module Methods
|
4
|
+
def should_receive_in_child_process(sym, opts={}, &block)
|
5
|
+
__mock_proxy.add_child_process_message_expectation(opts[:expected_from] || caller(1)[0], sym.to_sym, opts, &block)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module RSpec
|
12
|
+
module Mocks
|
13
|
+
module Methods
|
14
|
+
include RSpec::ProcessMocks::Methods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RSpec
|
2
|
+
module ProcessMocks
|
3
|
+
module Proxy
|
4
|
+
def add_child_process_message_expectation(location, method_name, opts={}, &block)
|
5
|
+
method_double[method_name].add_child_process_expectation @error_generator, @expectation_ordering, location, opts, &block
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module RSpec
|
12
|
+
module Mocks
|
13
|
+
class Proxy
|
14
|
+
include RSpec::ProcessMocks::Proxy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require "rspec/process_mocks/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rspec-process-mocks"
|
7
|
+
s.version = RSpec::ProcessMocks::Version::STRING
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Paul Cortens"]
|
10
|
+
s.email = "paul@thoughtless.ca"
|
11
|
+
s.homepage = "http://github.com/thoughtless/rspec-process-mocks"
|
12
|
+
s.summary = "rspec-process-mocks-#{RSpec::ProcessMocks::Version::STRING}"
|
13
|
+
s.description = "Add-on for RSpec's 'test double' framework, with support for stubbing and mocking within child processes"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.extra_rdoc_files = [ "README.md" ]
|
19
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
20
|
+
s.require_path = "lib"
|
21
|
+
end
|
22
|
+
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-process-mocks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Cortens
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-28 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Add-on for RSpec's 'test double' framework, with support for stubbing and mocking within child processes
|
18
|
+
email: paul@thoughtless.ca
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README.md
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- Gemfile
|
28
|
+
- README.md
|
29
|
+
- cucumber.yml
|
30
|
+
- features/message_expectations/expect_message.feature
|
31
|
+
- features/support/env.rb
|
32
|
+
- lib/rspec/process_mocks.rb
|
33
|
+
- lib/rspec/process_mocks/framework.rb
|
34
|
+
- lib/rspec/process_mocks/message_expectation.rb
|
35
|
+
- lib/rspec/process_mocks/method_double.rb
|
36
|
+
- lib/rspec/process_mocks/methods.rb
|
37
|
+
- lib/rspec/process_mocks/proxy.rb
|
38
|
+
- lib/rspec/process_mocks/version.rb
|
39
|
+
- rspec-process-mocks.gemspec
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/thoughtless/rspec-process-mocks
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.5.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: rspec-process-mocks-0.0.1
|
68
|
+
test_files: []
|
69
|
+
|