Skip to content

Commit 41c8014

Browse files
authored
Feature/Mock core (#2)
* Implemented mock core * Added integration tests * Updated circleci/codeclimate configs * Updated project documentation
1 parent 0a89576 commit 41c8014

14 files changed

+251
-14
lines changed

.circleci/config.yml

+8-8
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,20 @@ jobs:
112112
./cc-test-reporter before-build
113113
bundle exec rspec
114114
115-
# - run:
116-
# name: Creating CodeClimate test coverage report
117-
# command: |
118-
# ./cc-test-reporter format-coverage -t simplecov -o "coverage/codeclimate.$CIRCLE_NODE_INDEX.json"
115+
- run:
116+
name: Creating CodeClimate test coverage report
117+
command: |
118+
./cc-test-reporter format-coverage -t simplecov -o "coverage/codeclimate.$CIRCLE_NODE_INDEX.json"
119119
120120
- store_artifacts:
121121
name: Saving Simplecov coverage artifacts
122122
path: ~/ruby-rspec-mock/coverage
123123
destination: coverage
124124

125-
# - deploy:
126-
# name: Uploading CodeClimate test coverage report
127-
# command: |
128-
# ./cc-test-reporter sum-coverage --output - --parts $CIRCLE_NODE_TOTAL coverage/codeclimate.*.json | ./cc-test-reporter upload-coverage --debug --input -
125+
- deploy:
126+
name: Uploading CodeClimate test coverage report
127+
command: |
128+
./cc-test-reporter sum-coverage --output - --parts $CIRCLE_NODE_TOTAL coverage/codeclimate.*.json | ./cc-test-reporter upload-coverage --debug --input -
129129
130130
compatibility-ruby:
131131
parameters:

.codeclimate.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ checks:
99
plugins:
1010
rubocop:
1111
enabled: true
12-
channel: rubocop-1-65
12+
channel: rubocop-1-66
1313
config:
1414
file: .circleci/linter_configs/.rubocop.yml
1515

.reek.yml

+9
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,12 @@
33
detectors:
44
IrresponsibleModule:
55
enabled: false
6+
7+
BooleanParameter:
8+
exclude:
9+
- RSpec::Mock::Context#respond_to_missing?
10+
11+
ManualDispatch:
12+
exclude:
13+
- RSpec::Mock::Context#method_missing
14+
- RSpec::Mock::Context#respond_to_missing?

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Seamless migration from third-party mocks to RSpec built-in mocking framework
22

3-
[![Maintainability](https://api.codeclimate.com/v1/badges/5ea9da61ef468b8ad4c4/maintainability)](https://codeclimate.com/github/mocktools/ruby-rspec-mock/maintainability)
4-
[![Test Coverage](https://api.codeclimate.com/v1/badges/5ea9da61ef468b8ad4c4/test_coverage)](https://codeclimate.com/github/mocktools/ruby-rspec-mock/test_coverage)
3+
[![Maintainability](https://api.codeclimate.com/v1/badges/8a7a9ca7f590838bf02f/maintainability)](https://codeclimate.com/github/mocktools/ruby-rspec-mock/maintainability)
4+
[![Test Coverage](https://api.codeclimate.com/v1/badges/8a7a9ca7f590838bf02f/test_coverage)](https://codeclimate.com/github/mocktools/ruby-rspec-mock/test_coverage)
55
[![CircleCI](https://circleci.com/gh/mocktools/ruby-rspec-mock/tree/master.svg?style=svg)](https://circleci.com/gh/mocktools/ruby-rspec-mock/tree/master)
66
[![Gem Version](https://badge.fury.io/rb/rspec-mock.svg)](https://badge.fury.io/rb/rspec-mock)
77
[![Downloads](https://img.shields.io/gem/dt/rspec-mock.svg?colorA=004d99&colorB=0073e6)](https://rubygems.org/gems/rspec-mock)

lib/rspec/core/configuration.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
module RSpec
4+
module Core
5+
class Configuration
6+
include RSpec::Mock::Configuration
7+
end
8+
end
9+
end

lib/rspec/mock/configuration.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
module RSpec
4+
module Mock
5+
module Configuration
6+
def rspec_mock
7+
RSpec::Mocks.configuration.tap do |config|
8+
yield config if block_given?
9+
end
10+
end
11+
end
12+
end
13+
end

lib/rspec/mock/context.rb

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
module RSpec
4+
module Mock
5+
class Context
6+
include RSpec::Mocks::ExampleMethods
7+
include RSpec::Matchers
8+
9+
def initialize(example_group)
10+
@example_group = example_group
11+
end
12+
13+
def method_missing(method, *args, &block)
14+
if @example_group.respond_to?(method)
15+
@example_group.send(method, *args, &block)
16+
else
17+
super
18+
end
19+
end
20+
21+
def respond_to_missing?(method, include_private = false)
22+
@example_group.respond_to?(method, include_private) || super
23+
end
24+
end
25+
end
26+
end

lib/rspec/mock/core.rb

+10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
# frozen_string_literal: true
22

3+
require 'rspec/core'
4+
require 'rspec/mocks'
5+
36
module RSpec
47
module Mock
8+
require_relative 'configuration'
9+
require_relative 'context'
10+
require_relative 'methods'
511
require_relative 'version'
612
end
13+
14+
module Core
15+
require_relative '../core/configuration'
16+
end
717
end

lib/rspec/mock/methods.rb

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module RSpec
4+
module Mock
5+
module Methods
6+
def rspec_mock(&block)
7+
return unless block_given?
8+
9+
RSpec::Mocks.with_temporary_scope do
10+
RSpec::Mock::Context.new(self).instance_eval(&block)
11+
end
12+
end
13+
end
14+
end
15+
end

spec/rspec/mock/configuration_spec.rb

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RSpec::Mock::Configuration do
4+
let(:test_class) do
5+
Class.new do
6+
include RSpec::Mock::Configuration
7+
end
8+
end
9+
10+
let(:instance) { test_class.new }
11+
let(:mock_config) { instance_double('RSpec::Mocks::Configuration') }
12+
13+
before { allow(RSpec::Mocks).to receive(:configuration).and_return(mock_config) }
14+
15+
describe '#rspec_mock' do
16+
context 'when block is given' do
17+
it 'yields the configuration object' do
18+
expect(mock_config).to receive(:tap).and_yield(mock_config)
19+
expect { |block| instance.rspec_mock(&block) }.to yield_with_args(mock_config)
20+
end
21+
end
22+
23+
context 'when no block is given' do
24+
it 'returns the configuration object' do
25+
expect(mock_config).to receive(:tap)
26+
expect(instance.rspec_mock).to be_nil
27+
end
28+
end
29+
end
30+
end

spec/rspec/mock/context_spec.rb

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RSpec::Mock::Context do
4+
let(:example_group) { double('ExampleGroup') } # rubocop:disable RSpec/VerifiedDoubles
5+
let(:context) { described_class.new(example_group) }
6+
7+
describe '#initialize' do
8+
it 'stores the example group' do
9+
expect(context.instance_variable_get(:@example_group)).to eq(example_group)
10+
end
11+
end
12+
13+
describe '#method_missing' do
14+
context 'when example group responds to the method' do
15+
it 'delegates the method to example group' do
16+
expect(example_group).to receive(:respond_to?).with(:some_method).and_return(true)
17+
expect(example_group).to receive(:some_method).with('arg1', 'arg2').and_return('result')
18+
19+
result = context.some_method('arg1', 'arg2')
20+
expect(result).to eq('result')
21+
end
22+
end
23+
24+
context 'when example group does not respond to the method' do
25+
it 'raises NoMethodError' do
26+
expect(example_group).to receive(:respond_to?).with(:unknown_method)
27+
expect { context.unknown_method }.to raise_error(NoMethodError)
28+
end
29+
end
30+
end
31+
32+
describe '#respond_to_missing?' do
33+
context 'when example group responds to the method' do
34+
it 'returns true' do
35+
expect(example_group).to receive(:respond_to?).with(:some_method, false).and_return(true)
36+
expect(context.respond_to?(:some_method)).to be(true)
37+
end
38+
end
39+
40+
context 'when example group does not respond to the method' do
41+
it 'returns false' do
42+
expect(example_group).to receive(:respond_to?).with(:unknown_method, false).and_return(false)
43+
expect(context.respond_to?(:unknown_method)).to be(false)
44+
end
45+
end
46+
end
47+
end

spec/rspec/mock/methods_spec.rb

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RSpec::Mock::Methods do
4+
let(:test_class) do
5+
Class.new do
6+
include RSpec::Mock::Methods
7+
end
8+
end
9+
10+
let(:instance) { test_class.new }
11+
12+
describe '#rspec_mock' do
13+
let(:mock_context) { instance_double(RSpec::Mock::Context) }
14+
15+
before do
16+
allow(RSpec::Mock::Context).to receive(:new).and_return(mock_context)
17+
allow(mock_context).to receive(:instance_eval)
18+
end
19+
20+
context 'when block is given' do
21+
it 'executes within temporary scope, creates context and evaluates the block' do
22+
expect(RSpec::Mocks).to receive(:with_temporary_scope).and_yield
23+
expect(RSpec::Mock::Context).to receive(:new).with(instance).and_return(mock_context)
24+
expect(mock_context).to receive(:instance_eval)
25+
instance.rspec_mock { nil }
26+
end
27+
end
28+
29+
context 'when no block is given' do
30+
it 'does not create context or temporary scope' do
31+
expect(RSpec::Mock::Context).not_to receive(:new)
32+
expect(RSpec::Mocks).not_to receive(:with_temporary_scope)
33+
expect(instance.rspec_mock).to be_nil
34+
end
35+
end
36+
end
37+
end

spec/rspec/mock_spec.rb

+38-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
11
# frozen_string_literal: true
22

3-
RSpec.describe RSpec::Mock do
4-
it 'has a version number' do
5-
expect(described_class::VERSION).not_to be_nil
3+
RSpec.describe 'integration' do # rubocop:disable RSpec/DescribeClass
4+
subject(:service) { test_class.call(*args, **kwargs) }
5+
6+
let(:test_class) do
7+
Class.new do
8+
def self.call(*args, **kwargs)
9+
{ args: args, kwargs: kwargs }
10+
end
11+
end
12+
end
13+
let(:args) { [1, 2, 3] }
14+
let(:kwargs) { { a: 1, b: 2 } }
15+
let(:expected_result) { { args: args, kwargs: kwargs } }
16+
17+
context 'with before block' do
18+
before do
19+
rspec_mock do
20+
allow(test_class)
21+
.to receive(:call)
22+
.with(*args, **kwargs)
23+
.and_call_original
24+
end
25+
end
26+
27+
it { is_expected.to eq(expected_result) }
28+
end
29+
30+
context 'without before block' do
31+
it do
32+
rspec_mock do
33+
expect(test_class)
34+
.to receive(:call)
35+
.with(*args, **kwargs)
36+
.and_call_original
37+
38+
expect(service).to eq(expected_result)
39+
end
40+
end
641
end
742
end

spec/spec_helper.rb

+6
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,11 @@
1515
config.disable_monkey_patching!
1616
config.order = :random
1717

18+
config.rspec_mock do |mock|
19+
mock.verify_partial_doubles = true
20+
end
21+
22+
config.include RSpec::Mock::Methods
23+
1824
::Kernel.srand(config.seed)
1925
end

0 commit comments

Comments
 (0)