rspec-process-mocks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ doc
7
+ tmp
8
+ rerun.txt
9
+ Gemfile.lock
10
+ .bundle
11
+ .rvmrc
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,2 @@
1
+ default: --require features --tags ~@wip features --format progress
2
+ wip: --require features --tags @wip:3 --wip features
@@ -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,9 @@
1
+ require 'bundler'
2
+ Bundler.require(:default, :development, :test)
3
+
4
+ require 'aruba/cucumber'
5
+ require 'rspec/expectations'
6
+
7
+ Before do
8
+ @aruba_timeout_seconds = 3
9
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec/process_mocks/methods'
2
+ require 'rspec/process_mocks/proxy'
3
+ require 'rspec/process_mocks/method_double'
4
+ require 'rspec/process_mocks/message_expectation'
@@ -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,7 @@
1
+ module RSpec # :nodoc:
2
+ module ProcessMocks # :nodoc:
3
+ module Version # :nodoc:
4
+ STRING = '0.0.1'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec/process_mocks/framework'
2
+ require 'rspec/process_mocks/version'
3
+
4
+ module RSpec
5
+ module ProcessMocks
6
+ end
7
+ 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
+