model_mocker 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +38 -0
- data/Rakefile +51 -0
- data/lib/model_mocker.rb +120 -0
- data/lib/model_mocker/railtie.rb +10 -0
- data/rails/init.rb +5 -0
- data/spec/model_mocker_spec.rb +47 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +33 -0
- metadata +110 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Matt Patterson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= ModelMocker
|
2
|
+
|
3
|
+
This plugin gives you an easy way of generating partially mocked ActiveRecord model objects. This is a useful way of simulating some aspects of a model (like the persistence layer) but leaving the domain logic parts intact, so that you test those in isolation, without worry about database reads and writes.
|
4
|
+
|
5
|
+
It's similar to RSpec's mock_model method, but gives you a slightly more declarative way of going about things. I found myself wanting something along these lines a long time ago, and created something very simple that I've used in several projects. More recently I've found myself wanting to easily control certain kinds of behaviour in controller specs, like wanting to ensure that a model instance is valid to allow easy and declarative speccing of the controller.
|
6
|
+
|
7
|
+
The plugin adds a <tt>mock</tt> method to <tt>ActiveRecord::Base</tt> when <tt>mock_models</tt> is required, which behaves a lot like <tt>AR::Base.new</tt> or <tt>AR::Base.create</tt>, except that you can specify an ID too. Special behaviour is added by passing a block to <tt>mock</tt> and calling methods on the object it yields.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
For Rails 2, add <tt>model_mocker</tt> to your <tt>config.gem</tt> for the test evironment.
|
12
|
+
|
13
|
+
For Rails 3, add <tt>model_mocker</tt> to your +Gemfile+ in the <tt>:test</tt> group.
|
14
|
+
|
15
|
+
== Requirements
|
16
|
+
|
17
|
+
ModelMocker uses the Mocha[http://mocha.rubyforge.org/] Gem.
|
18
|
+
|
19
|
+
== Examples
|
20
|
+
|
21
|
+
To create a mocked model instance which behaves as it's been fetched from the DB:
|
22
|
+
|
23
|
+
ModelClass.mock(:id => 1)
|
24
|
+
|
25
|
+
To create one which behaves as if it's a new record
|
26
|
+
|
27
|
+
ModelClass.mock { |m| m.as_new_record }
|
28
|
+
|
29
|
+
One which is new and will always report that is invalid, whether or not ActiveRecord validations have been met or not.
|
30
|
+
|
31
|
+
ModelClass.mock do |m|
|
32
|
+
m.as_new_record
|
33
|
+
m.invalid
|
34
|
+
end
|
35
|
+
|
36
|
+
The RDoc for the instance methods on ModelMocker gives a complete rundown of what's possible.
|
37
|
+
|
38
|
+
Copyright (c) 2008 Matt Patterson, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
rspec_base = File.expand_path(File.dirname(__FILE__) + '/../rspec/lib')
|
4
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
desc 'Default: run specs.'
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
desc 'Generate documentation for the model_mocker plugin.'
|
11
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
13
|
+
rdoc.title = 'ModelMocker'
|
14
|
+
rdoc.options << '--line-numbers' << '--inline-source' #<< '--main' << 'ModelMocker'
|
15
|
+
rdoc.rdoc_files.include('README.rdoc')
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
end
|
18
|
+
|
19
|
+
task :stats => "spec:statsetup"
|
20
|
+
|
21
|
+
desc "Run all specs in spec directory"
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
23
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
24
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
namespace :spec do
|
28
|
+
desc "Run all specs in spec directory with RCov"
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
30
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
31
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
32
|
+
t.rcov = true
|
33
|
+
t.rcov_opts = lambda do
|
34
|
+
IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Print Specdoc for all specs (excluding plugin specs)"
|
39
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
40
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
41
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
42
|
+
end
|
43
|
+
|
44
|
+
# Setup specs for stats
|
45
|
+
task :statsetup do
|
46
|
+
require 'code_statistics'
|
47
|
+
::STATS_DIRECTORIES << %w(All\ specs spec)
|
48
|
+
::CodeStatistics::TEST_TYPES << "All specs"
|
49
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
50
|
+
end
|
51
|
+
end
|
data/lib/model_mocker.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'mocha'
|
2
|
+
|
3
|
+
# class ActiveRecord::Base
|
4
|
+
# # Create a new instance of the class, pass in creation params and then
|
5
|
+
# # yield the ModelMocker instance so that methods can be called on it
|
6
|
+
# def self.mock(params = {})
|
7
|
+
# mock_model = ModelMocker.new(self, params)
|
8
|
+
# yield(mock_model) if block_given?
|
9
|
+
# mock_model.instance
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
|
13
|
+
# An easy way of generating partially mocked <tt>ActiveRecord</tt> model objects.
|
14
|
+
# This is a useful way of simulating some aspects of a model (like the
|
15
|
+
# persistence layer) but leaving the domain logic parts intact, so that
|
16
|
+
# you can test those in isolation, without worrying about database reads
|
17
|
+
# and writes.
|
18
|
+
class ModelMocker
|
19
|
+
# Provides instance method replacements for the <tt>ActiveRecord::Base</tt> instances created by <tt>#mock</tt>
|
20
|
+
module Helpers
|
21
|
+
def connection
|
22
|
+
raise StandardError, "mock model instances are not allowed to access the database"
|
23
|
+
end
|
24
|
+
|
25
|
+
def new_record?
|
26
|
+
id.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ActiveRecordHook
|
31
|
+
# Create a new instance of the class, pass in creation params and then
|
32
|
+
# yield the ModelMocker instance so that methods can be called on it
|
33
|
+
def self.mock(params = {})
|
34
|
+
mock_model = ModelMocker.new(self, params)
|
35
|
+
yield(mock_model) if block_given?
|
36
|
+
mock_model.instance
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# ModelMocker.new is called with the AR::Base subclass a mock object is for,
|
41
|
+
# with any creation params in stub_params. Methods on the ModelMocker instance
|
42
|
+
# determine how the mock AR::Base subclass object will behave
|
43
|
+
def initialize(klass, stub_params = {})
|
44
|
+
@klass = klass
|
45
|
+
@stub_params = stub_params
|
46
|
+
end
|
47
|
+
|
48
|
+
def instance # :nodoc:
|
49
|
+
return @instance unless @instance.nil?
|
50
|
+
id = @stub_params[:id]
|
51
|
+
@instance = @klass.new
|
52
|
+
attributes.each do |k, v|
|
53
|
+
@instance.send(:write_attribute, k, v)
|
54
|
+
end
|
55
|
+
@instance.stubs(:id).returns(id)
|
56
|
+
stub_instance_methods!
|
57
|
+
@instance.extend(ModelMocker::Helpers)
|
58
|
+
@instance
|
59
|
+
end
|
60
|
+
|
61
|
+
# The model instance will report that it's a new record
|
62
|
+
def as_new_record
|
63
|
+
instance.stubs(:new_record?).returns(true)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Validity-related methods will always report that the instance is valid,
|
67
|
+
# and <tt>#save</tt> and <tt>#save!</tt> will return true (without actually saving anything)
|
68
|
+
def valid
|
69
|
+
instance.stubs(:valid?).returns(true)
|
70
|
+
instance.stubs(:save).returns(true)
|
71
|
+
instance.stubs(:save!).returns(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Validity-related methods will always report that the instance is invalid,
|
75
|
+
# <tt>#save</tt> will return false, and <tt>#save!</tt> will raise <tt>ActiveRecord::RecordNotSaved</tt>
|
76
|
+
def invalid
|
77
|
+
instance.stubs(:valid?).returns(false)
|
78
|
+
instance.stubs(:save).returns(false)
|
79
|
+
instance.stubs(:save!).raises(ActiveRecord::RecordNotSaved)
|
80
|
+
end
|
81
|
+
|
82
|
+
# The model instance cannot be updated: <tt>#update_attributes</tt> will always return false
|
83
|
+
def cannot_be_updated
|
84
|
+
instance.stubs(:update_attributes).returns(false)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The model instance will return true if <tt>#destroy</tt> is called
|
88
|
+
def can_be_destroyed
|
89
|
+
instance.stubs(:destroy).returns(true)
|
90
|
+
end
|
91
|
+
|
92
|
+
# The model instance will return false if <tt>#destroy</tt> is called
|
93
|
+
def cannot_be_destroyed
|
94
|
+
instance.stubs(:destroy).returns(false)
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def valid_columns # :nodoc:
|
100
|
+
@valid_columns ||= @klass.columns_hash.collect { |k,v| k }
|
101
|
+
end
|
102
|
+
|
103
|
+
def attributes # :nodoc:
|
104
|
+
Hash[*(@stub_params.select { |attr_name, value| valid_columns.include?(attr_name.to_s) }.flatten)].reject {|k, v| k.to_s == 'id'}
|
105
|
+
end
|
106
|
+
|
107
|
+
def method_stubs # :nodoc:
|
108
|
+
Hash[*(@stub_params.reject { |attr_name, value| valid_columns.include?(attr_name.to_s) }.collect { |k, v| [k.to_sym, v] }).flatten]
|
109
|
+
end
|
110
|
+
|
111
|
+
def stub_instance_methods! # :nodoc:
|
112
|
+
method_stubs.each do |meth, value|
|
113
|
+
instance.stubs(meth).returns(value)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if defined?(Rails::Railtie)
|
119
|
+
require 'model_mocker/railtie'
|
120
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
|
2
|
+
|
3
|
+
class FakeARClass
|
4
|
+
class << self
|
5
|
+
def columns_hash
|
6
|
+
{'id' => :id, 'thing' => :thing, 'wotsit' => :wotsit}
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ModelMocker do
|
12
|
+
describe "working out which args are attributes and which are method stubs" do
|
13
|
+
before(:each) do
|
14
|
+
@mm = ModelMocker.new(FakeARClass, {:id => 1, :thing => 'value', :wotsit => 'value', :gubbins => 'value'})
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should figure out that :gubbins is not an attribute" do
|
18
|
+
ModelMocker.publicize_methods do
|
19
|
+
@mm.attributes.should == {:thing => 'value', :wotsit => 'value'}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should figure that :gubbins should be a method stub" do
|
24
|
+
ModelMocker.publicize_methods do
|
25
|
+
@mm.method_stubs.should == {:gubbins => 'value'}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "working out which args are attributes and which are method stubs when the keys are strings not symbols" do
|
31
|
+
before(:each) do
|
32
|
+
@mm = ModelMocker.new(FakeARClass, {'id' => 1, 'thing' => 'value', 'wotsit' => 'value', 'gubbins' => 'value'})
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should figure out that 'gubbins' is not an attribute" do
|
36
|
+
ModelMocker.publicize_methods do
|
37
|
+
@mm.attributes.should == {'thing' => 'value', 'wotsit' => 'value'}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should figure that 'gubbins' should be a method stub, and called :gubbins" do
|
42
|
+
ModelMocker.publicize_methods do
|
43
|
+
@mm.method_stubs.should == { :gubbins => 'value' }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
lib_path = File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
$LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
|
8
|
+
gem 'activerecord'
|
9
|
+
require 'active_record'
|
10
|
+
require 'model_mocker'
|
11
|
+
|
12
|
+
|
13
|
+
Spec::Runner.configure do |config|
|
14
|
+
config.mock_with :mocha
|
15
|
+
end
|
16
|
+
|
17
|
+
class Class
|
18
|
+
def publicize_methods
|
19
|
+
saved_private_instance_methods = self.private_instance_methods
|
20
|
+
saved_protected_instance_methods = self.protected_instance_methods
|
21
|
+
self.class_eval do
|
22
|
+
public *saved_private_instance_methods
|
23
|
+
public *saved_protected_instance_methods
|
24
|
+
end
|
25
|
+
|
26
|
+
yield
|
27
|
+
|
28
|
+
self.class_eval do
|
29
|
+
private *saved_private_instance_methods
|
30
|
+
protected *saved_protected_instance_methods
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: model_mocker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 1.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Matt Patterson
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-02 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: activerecord
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 2
|
43
|
+
- 3
|
44
|
+
version: "2.3"
|
45
|
+
type: :runtime
|
46
|
+
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: mocha
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
- 9
|
57
|
+
- 8
|
58
|
+
version: 0.9.8
|
59
|
+
type: :runtime
|
60
|
+
version_requirements: *id003
|
61
|
+
description: Declarative partial mocking for ActiveRecord
|
62
|
+
email: matt@reprocessed.org
|
63
|
+
executables: []
|
64
|
+
|
65
|
+
extensions: []
|
66
|
+
|
67
|
+
extra_rdoc_files:
|
68
|
+
- MIT-LICENSE
|
69
|
+
- README.rdoc
|
70
|
+
files:
|
71
|
+
- README.rdoc
|
72
|
+
- MIT-LICENSE
|
73
|
+
- Rakefile
|
74
|
+
- lib/model_mocker/railtie.rb
|
75
|
+
- lib/model_mocker.rb
|
76
|
+
- rails/init.rb
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/fidothe/model_mocker
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options:
|
83
|
+
- --charset=UTF-8
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
requirements: []
|
101
|
+
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.3.6
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Declarative partial mocking for ActiveRecord
|
107
|
+
test_files:
|
108
|
+
- spec/spec.opts
|
109
|
+
- spec/model_mocker_spec.rb
|
110
|
+
- spec/spec_helper.rb
|