activerecord-nulldb-adapter 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2008 Avdi Grimm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,143 @@
1
+ = The NullDB Connection Adapter Plugin
2
+
3
+ == What
4
+
5
+ NullDB is the Null Object pattern as applied to ActiveRecord database
6
+ adapters. It is a database backend that translates database
7
+ interactions into no-ops. Using NullDB enables you to test your model
8
+ business logic - including +after_save+ hooks - without ever touching
9
+ a real database.
10
+
11
+ == How
12
+
13
+ Once installed, NullDB can be used much like any other ActiveRecord
14
+ database adapter:
15
+
16
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
17
+
18
+ NullDB needs to know where you keep your schema file in order to
19
+ reflect table metadata. By default it looks in
20
+ RAILS_ROOT/db/schema.rb. You can override that by setting the
21
+ +schema+ option:
22
+
23
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
24
+ :schema => foo/myschema.rb
25
+
26
+ NullDB comes with RSpec integration. To replace the database with
27
+ NullDB in all of your specs, put the following in your
28
+ spec/spec_helper:
29
+
30
+ require 'nulldb_rspec'
31
+ include NullDB::RSpec::NullifiedDatabase
32
+
33
+ Or if you just want to use NullDB in a specific spec context, you can
34
+ include the same module inside a context:
35
+
36
+ require 'nulldb_rspec'
37
+
38
+ describe Employee, "with access to the database" do
39
+ fixtures :employees
40
+ # ...
41
+ end
42
+
43
+ describe Employee, "with NullDB" do
44
+ include NullDB::RSpec::NullifiedDatabase
45
+ # ...
46
+ end
47
+
48
+ NullDB::Rspec provides some custom matcher support for verifying
49
+ expectations about interactions with the database:
50
+
51
+ describe Employee do
52
+ include NullDB::RSpec::NullifiedDatabase
53
+
54
+ it "should cause an insert statement to be executed" do
55
+ Employee.create!
56
+ Employee.connection.should have_executed(:insert)
57
+ end
58
+ end
59
+
60
+ UnitRecord-style verification that no database calls have been made at
61
+ all can be achieved by using the special +:anything+ symbol:
62
+
63
+ describe "stuff that shouldn't touch the database" do
64
+ after :each do
65
+ Employee.connection.should_not have_executed(:anything)
66
+ end
67
+ # ...
68
+ end
69
+
70
+ You can also experiment with putting NullDB in your database.yml:
71
+
72
+ unit_test:
73
+ adapter: nulldb
74
+
75
+ However, due to the way Rails hard-codes specific database adapters
76
+ into its standard Rake tasks, you may find that this generates
77
+ unexpected and difficult-to-debug behavior. Workarounds for this are
78
+ under development.
79
+
80
+ == Why
81
+
82
+ There are a number of advantages to writing unit tests that never
83
+ touch the database. The biggest is probably speed of execution - unit
84
+ tests must be fast for test-driven development to be practical.
85
+ Another is separation of concerns: unit tests should be exercising
86
+ only the business logic contained in your models, not ActiveRecord.
87
+ For more on why testing-sans-database is a god idea, see:
88
+ http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database.
89
+
90
+ NullDB is one way to separate your unit tests from the database. It
91
+ was inspired by the ARBS[http://arbs.rubyforge.org/] and
92
+ UnitRecord[http://unit-test-ar.rubyforge.org/] libraries. It differs
93
+ from them in a couple of ways:
94
+
95
+ 1. It works. At the time of writing both ARBS and UnitRecord were
96
+ not working for me out of the box with Rails 2.0.
97
+
98
+ 2. It avoids monkey-patching as much as possible. Rather than
99
+ re-wiring the secret inner workings of ActiveRecord (and thus being
100
+ tightly coupled to those inner workings), NullDB implements the
101
+ same [semi-]well-documented public interface that the other standard
102
+ database adapters, like MySQL and SQLServer, implement.
103
+
104
+ 3. UnitRecord takes the approach of eliminating database interaction
105
+ in tests by turning almost every database interaction into an
106
+ exception. NullDB recognizes that ActiveRecord objects typically
107
+ can't take two steps without consulting the database, so instead it
108
+ turns database interactions into no-ops.
109
+
110
+ One concrete advantage of this null-object pattern design is that it
111
+ is possible with NullDB to test +after_save+ hooks. With NullDB, you
112
+ can call +#save+ and all of the usual callbacks will be called - but
113
+ nothing will be saved.
114
+
115
+ == Limitations
116
+
117
+ * It is *not* an in-memory database. Finds will not work. Neither
118
+ will +reload+, currently. Test fixtures won't work either, for
119
+ obvious reasons.
120
+ * It has only the most rudimentery schema/migration support. Complex
121
+ migrations will probably break it.
122
+ * Lots of other things probably don't work. Patches welcome!
123
+
124
+ == Who
125
+
126
+ NullDB was written by Avdi Grimm <mailto:avdi@avdi.org>
127
+
128
+ == Where
129
+
130
+ * Homepage: http://nulldb.rubyforge.org
131
+ * Project Info: http://rubyforge.org/projects/nulldb/
132
+ * SCM: http://rubyforge.org/scm/?group_id=7512
133
+
134
+ == Changes
135
+
136
+ * Version 0.0.1 (2007-02-18)
137
+ - Initial Release
138
+ * Version 0.0.2 (2007-05-31)
139
+ - Moved to Rubyforge
140
+
141
+ == License
142
+
143
+ See the LICENSE file for licensing information.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ # active_record connection_adapters abstract connection_specification
6
+ GEM_NAME = 'activerecord-nulldb-adapter'
7
+
8
+ desc "Run all examples"
9
+ Spec::Rake::SpecTask.new('spec') do |t|
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ Rake::RDocTask.new do |rd|
14
+ rd.main = "README"
15
+ rd.rdoc_files.include("README", "LICENSE", "lib/**/*.rb")
16
+ end
17
+
18
+ desc "Publish project home page"
19
+ task :publish => ["rdoc"] do
20
+ sh "scp -r html/* avdi@rubyforge.org:/var/www/gforge-projects/nulldb"
21
+ end
22
+
23
+ desc "Tag release"
24
+ task :tag do
25
+ warn "This needs to be updated for git"
26
+ exit 1
27
+ repos = "http://svn.avdi.org/nulldb"
28
+ version = ENV["VERSION"]
29
+ raise "No version specified" unless version
30
+ sh "svn cp #{repos}/trunk #{repos}/tags/nulldb-#{version}"
31
+ end
32
+
33
+ desc "Build gem"
34
+ task :gem do
35
+ system 'rake gemspec'
36
+ system "gem build #{GEM_NAME}.gemspec"
37
+ end
38
+
39
+ begin
40
+ require 'jeweler'
41
+ Jeweler::Tasks.new do |gem|
42
+ gem.name = GEM_NAME
43
+ gem.summary = %Q{NullDB lets you to test your models without ever touching a real database.}
44
+ gem.email = "avdi@avdi.org"
45
+ gem.homepage = 'http://nulldb.rubyforge.org'
46
+ gem.description = "An ActiveRecord null database adapter for greater speed and isolation in unit tests"
47
+ gem.rubyforge_project = 'nulldb'
48
+ gem.authors = ['Avdi Grimm']
49
+ end
50
+
51
+ rescue LoadError
52
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{activerecord-nulldb-adapter}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Avdi Grimm"]
12
+ s.date = %q{2009-12-30}
13
+ s.description = %q{An ActiveRecord null database adapter for greater speed and isolation in unit tests}
14
+ s.email = %q{avdi@avdi.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "activerecord-nulldb-adapter.gemspec",
25
+ "init.rb",
26
+ "lib/active_record/connection_adapters/nulldb_adapter.rb",
27
+ "lib/nulldb_rspec.rb",
28
+ "spec/nulldb_spec.rb",
29
+ "tasks/database.rake"
30
+ ]
31
+ s.homepage = %q{http://nulldb.rubyforge.org}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubyforge_project = %q{nulldb}
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{NullDB lets you to test your models without ever touching a real database.}
37
+ s.test_files = [
38
+ "spec/nulldb_spec.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ else
47
+ end
48
+ else
49
+ end
50
+ end
51
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ #require 'active_record/connection_adapters/nulldb_adapter'
@@ -0,0 +1,231 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+ require 'singleton'
4
+ require 'active_record/connection_adapters/abstract_adapter'
5
+
6
+ class ActiveRecord::Base
7
+ # Instantiate a new NullDB connection. Used by ActiveRecord internally.
8
+ def self.nulldb_connection(config)
9
+ ActiveRecord::ConnectionAdapters::NullDBAdapter.new(config)
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ # Just make sure you have the latest version of your schema
15
+ class Schema < Migration
16
+ def self.define(info={}, &block)
17
+ instance_eval(&block)
18
+ end
19
+ end
20
+ end
21
+
22
+ class ActiveRecord::ConnectionAdapters::NullDBAdapter <
23
+ ActiveRecord::ConnectionAdapters::AbstractAdapter
24
+
25
+ class Statement
26
+ attr_reader :entry_point, :content
27
+
28
+ def initialize(entry_point, content = "")
29
+ @entry_point, @content = entry_point, content
30
+ end
31
+
32
+ def ==(other)
33
+ self.entry_point == other.entry_point
34
+ end
35
+ end
36
+
37
+ class Checkpoint < Statement
38
+ def initialize
39
+ super(:checkpoint, "")
40
+ end
41
+
42
+ def ==(other)
43
+ self.class == other.class
44
+ end
45
+ end
46
+
47
+ TableDefinition = ActiveRecord::ConnectionAdapters::TableDefinition
48
+
49
+ class NullObject
50
+ def method_missing(*args, &block)
51
+ nil
52
+ end
53
+ end
54
+
55
+ # A convenience method for integratinginto RSpec. See README for example of
56
+ # use.
57
+ def self.insinuate_into_spec(config)
58
+ config.before :all do
59
+ ActiveRecord::Base.establish_connection(:adapter => :nulldb)
60
+ end
61
+
62
+ config.after :all do
63
+ ActiveRecord::Base.establish_connection(:test)
64
+ end
65
+ end
66
+
67
+ # Recognized options:
68
+ #
69
+ # [+:schema+] path to the schema file, relative to RAILS_ROOT
70
+ def initialize(config={})
71
+ @log = StringIO.new
72
+ @logger = Logger.new(@log)
73
+ @last_unique_id = 0
74
+ @tables = {'schema_info' => TableDefinition.new(nil)}
75
+ @schema_path = config.fetch(:schema){ "db/schema.rb" }
76
+ super(nil, @logger)
77
+ end
78
+
79
+ # A log of every statement that has been "executed" by this connection adapter
80
+ # instance.
81
+ def execution_log
82
+ (@execution_log ||= [])
83
+ end
84
+
85
+ # A log of every statement that has been "executed" since the last time
86
+ # #checkpoint! was called, or since the connection was created.
87
+ def execution_log_since_checkpoint
88
+ checkpoint_index = @execution_log.rindex(Checkpoint.new)
89
+ checkpoint_index = checkpoint_index ? checkpoint_index + 1 : 0
90
+ @execution_log[(checkpoint_index..-1)]
91
+ end
92
+
93
+ # Inserts a checkpoint in the log. See also #execution_log_since_checkpoint.
94
+ def checkpoint!
95
+ self.execution_log << Checkpoint.new
96
+ end
97
+
98
+ def adapter_name
99
+ "NullDB"
100
+ end
101
+
102
+ def supports_migrations?
103
+ true
104
+ end
105
+
106
+ def create_table(table_name, options = {})
107
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(self)
108
+ unless options[:id] == false
109
+ table_definition.primary_key(options[:primary_key] || "id")
110
+ end
111
+
112
+ yield table_definition
113
+
114
+ @tables[table_name] = table_definition
115
+ end
116
+
117
+ def add_fk_constraint(*args)
118
+ # NOOP
119
+ end
120
+
121
+ def add_pk_constraint(*args)
122
+ # NOOP
123
+ end
124
+
125
+ # Retrieve the table names defined by the schema
126
+ def tables
127
+ @tables.keys.map(&:to_s)
128
+ end
129
+
130
+ # Retrieve table columns as defined by the schema
131
+ def columns(table_name, name = nil)
132
+ if @tables.size <= 1
133
+ ActiveRecord::Migration.verbose = false
134
+ Kernel.load(File.join(RAILS_ROOT, @schema_path))
135
+ end
136
+ table = @tables[table_name]
137
+ table.columns.map do |col_def|
138
+ ActiveRecord::ConnectionAdapters::Column.new(col_def.name.to_s,
139
+ col_def.default,
140
+ col_def.type,
141
+ col_def.null)
142
+ end
143
+ end
144
+
145
+ def execute(statement, name = nil)
146
+ self.execution_log << Statement.new(entry_point, statement)
147
+ NullObject.new
148
+ end
149
+
150
+ def select_rows(statement, name = nil)
151
+ returning([]) do
152
+ self.execution_log << Statement.new(entry_point, statement)
153
+ end
154
+ end
155
+
156
+ def insert(statement, name, primary_key, object_id, sequence_name)
157
+ returning(object_id || next_unique_id) do
158
+ with_entry_point(:insert) do
159
+ super(statement, name, primary_key, object_id, sequence_name)
160
+ end
161
+ end
162
+ end
163
+
164
+ def update(statement, name=nil)
165
+ with_entry_point(:update) do
166
+ super(statement, name)
167
+ end
168
+ end
169
+
170
+ def delete(statement, name=nil)
171
+ with_entry_point(:delete) do
172
+ super(statement, name)
173
+ end
174
+ end
175
+
176
+ def select_all(statement, name=nil)
177
+ with_entry_point(:select_all) do
178
+ super(statement, name)
179
+ end
180
+ end
181
+
182
+ def select_one(statement, name=nil)
183
+ with_entry_point(:select_one) do
184
+ super(statement, name)
185
+ end
186
+ end
187
+
188
+ def select_value(statement, name=nil)
189
+ with_entry_point(:select_value) do
190
+ super(statement, name)
191
+ end
192
+ end
193
+
194
+ protected
195
+
196
+ def select(statement, name)
197
+ returning([]) do
198
+ self.execution_log << Statement.new(entry_point, statement)
199
+ end
200
+ end
201
+
202
+ private
203
+
204
+ def next_unique_id
205
+ @last_unique_id += 1
206
+ end
207
+
208
+ def with_entry_point(method)
209
+ if entry_point.nil?
210
+ with_thread_local_variable(:entry_point, method) do
211
+ yield
212
+ end
213
+ else
214
+ yield
215
+ end
216
+ end
217
+
218
+ def entry_point
219
+ Thread.current[:entry_point]
220
+ end
221
+
222
+ def with_thread_local_variable(name, value)
223
+ old_value = Thread.current[name]
224
+ Thread.current[name] = value
225
+ begin
226
+ yield
227
+ ensure
228
+ Thread.current[name] = old_value
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,93 @@
1
+ require 'active_record/connection_adapters/nulldb_adapter'
2
+
3
+ module NullDB
4
+ module RSpec
5
+ end
6
+ end
7
+
8
+ module NullDB::RSpec::NullifiedDatabase
9
+ NullDBAdapter = ActiveRecord::ConnectionAdapters::NullDBAdapter
10
+
11
+ class HaveExecuted
12
+
13
+ def initialize(entry_point)
14
+ @entry_point = entry_point
15
+ end
16
+
17
+ def matches?(connection)
18
+ log = connection.execution_log_since_checkpoint
19
+ if entry_point == :anything
20
+ not log.empty?
21
+ else
22
+ log.include?(NullDBAdapter::Statement.new(@entry_point))
23
+ end
24
+ end
25
+
26
+ def description
27
+ "connection should execute #{@entry_point} statement"
28
+ end
29
+
30
+ def failure_message
31
+ " did not execute #{@entry_point} statement when it should have"
32
+ end
33
+
34
+ def negative_failure_message
35
+ " executed #{@entry_point} statement when it should not have"
36
+ end
37
+ end
38
+
39
+ def self.globally_nullify_database
40
+ Spec::Runner.configure do |config|
41
+ nullify_database(config)
42
+ end
43
+ end
44
+
45
+ def self.contextually_nullify_database(context)
46
+ nullify_database(context)
47
+ end
48
+
49
+ # A matcher for asserting that database statements have (or have not) been
50
+ # executed. Usage:
51
+ #
52
+ # ActiveRecord::Base.connection.should have_executed(:insert)
53
+ #
54
+ # The types of statement that can be matched mostly mirror the public
55
+ # operations available in
56
+ # ActiveRecord::ConnectionAdapters::DatabaseStatements:
57
+ # - :select_one
58
+ # - :select_all
59
+ # - :select_value
60
+ # - :insert
61
+ # - :update
62
+ # - :delete
63
+ # - :execute
64
+ #
65
+ # There is also a special :anything symbol that will match any operation.
66
+ def have_executed(entry_point)
67
+ HaveExecuted.new(entry_point)
68
+ end
69
+
70
+ private
71
+
72
+ def self.included(other)
73
+ if other.ancestors.include?(ActiveSupport::TestCase)
74
+ contextually_nullify_database(other)
75
+ else
76
+ globally_nullify_database
77
+ end
78
+ end
79
+
80
+ def self.nullify_database(receiver)
81
+ receiver.before :all do
82
+ ActiveRecord::Base.establish_connection(:adapter => :nulldb)
83
+ end
84
+
85
+ receiver.before :each do
86
+ ActiveRecord::Base.connection.checkpoint!
87
+ end
88
+
89
+ receiver.after :all do
90
+ ActiveRecord::Base.establish_connection(:test)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,189 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'active_record'
4
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
5
+
6
+ class Employee < ActiveRecord::Base
7
+ after_save :on_save_finished
8
+
9
+ def on_save_finished
10
+ end
11
+ end
12
+
13
+ RAILS_ROOT = "RAILS_ROOT"
14
+
15
+ describe "NullDB with no schema pre-loaded" do
16
+ before :each do
17
+ Kernel.stub!(:load)
18
+ ActiveRecord::Migration.stub!(:verbose=)
19
+ end
20
+
21
+ it "should load RAILS_ROOT/db/schema.rb if no alternate is specified" do
22
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
23
+ Kernel.should_receive(:load).with("RAILS_ROOT/db/schema.rb")
24
+ ActiveRecord::Base.connection.columns('schema_info')
25
+ end
26
+
27
+ it "should load the specified schema relative to RAILS_ROOT" do
28
+ Kernel.should_receive(:load).with("RAILS_ROOT/foo/myschema.rb")
29
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
30
+ :schema => "foo/myschema.rb"
31
+ ActiveRecord::Base.connection.columns('schema_info')
32
+ end
33
+
34
+ it "should suppress migration output" do
35
+ ActiveRecord::Migration.should_receive(:verbose=).with(false)
36
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
37
+ :schema => "foo/myschema.rb"
38
+ ActiveRecord::Base.connection.columns('schema_info')
39
+ end
40
+ end
41
+
42
+ describe "NullDB" do
43
+ before :all do
44
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
45
+ ActiveRecord::Migration.verbose = false
46
+ ActiveRecord::Schema.define do
47
+ create_table(:employees) do |t|
48
+ t.string :name
49
+ t.date :hire_date
50
+ t.integer :employee_number
51
+ t.decimal :salary
52
+ end
53
+
54
+ add_fk_constraint "foo", "bar", "baz", "buz", "bungle"
55
+ add_pk_constraint "foo", "bar", {}, "baz", "buz"
56
+ end
57
+ end
58
+
59
+ before :each do
60
+ @employee = Employee.new(:name => "John Smith",
61
+ :hire_date => Date.civil(2000, 1, 1),
62
+ :employee_number => 42,
63
+ :salary => 56000.00)
64
+ end
65
+
66
+ it "should enable instantiation of AR objects without a database" do
67
+ @employee.should_not be_nil
68
+ @employee.should be_a_kind_of(ActiveRecord::Base)
69
+ end
70
+
71
+ it "should remember columns defined in migrations" do
72
+ should_have_column(Employee, :name, :string)
73
+ should_have_column(Employee, :hire_date, :date)
74
+ should_have_column(Employee, :employee_number, :integer)
75
+ should_have_column(Employee, :salary, :decimal)
76
+ end
77
+
78
+ it "should enable simulated saving of AR objects" do
79
+ lambda { @employee.save! }.should_not raise_error
80
+ end
81
+
82
+ it "should enable AR callbacks during simulated save" do
83
+ @employee.should_receive(:on_save_finished)
84
+ @employee.save
85
+ end
86
+
87
+ it "should enable simulated deletes of AR objects" do
88
+ lambda { @employee.destroy }.should_not raise_error
89
+ end
90
+
91
+ it "should enable simulated creates of AR objects" do
92
+ emp = Employee.create(:name => "Bob Jones")
93
+ emp.name.should == "Bob Jones"
94
+ end
95
+
96
+ it "should generate new IDs when inserting unsaved objects" do
97
+ cxn = Employee.connection
98
+ id1 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
99
+ id2 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
100
+ id2.should == (id1 + 1)
101
+ end
102
+
103
+ it "should re-use object ID when inserting saved objects" do
104
+ cxn = Employee.connection
105
+ id1 = cxn.insert("some sql", "SomeClass Create", "id", 23, nil)
106
+ id1.should == 23
107
+ end
108
+
109
+ it "should log executed SQL statements" do
110
+ cxn = @employee.connection
111
+ exec_count = cxn.execution_log.size
112
+ @employee.save!
113
+ cxn.execution_log.size.should == (exec_count + 1)
114
+ end
115
+
116
+ it "should have the adapter name 'NullDB'" do
117
+ @employee.connection.adapter_name.should == "NullDB"
118
+ end
119
+
120
+ it "should support migrations" do
121
+ @employee.connection.supports_migrations?.should be_true
122
+ end
123
+
124
+ it "should always have a schema_info table definition" do
125
+ @employee.connection.tables.should include("schema_info")
126
+ end
127
+
128
+ it "should return an empty array from #select" do
129
+ @employee.connection.select_all("who cares", "blah").should == []
130
+ end
131
+
132
+ it "should provide a way to set log checkpoints" do
133
+ cxn = @employee.connection
134
+ @employee.save!
135
+ cxn.execution_log_since_checkpoint.size.should > 0
136
+ cxn.checkpoint!
137
+ cxn.execution_log_since_checkpoint.size.should == 0
138
+ @employee.save!
139
+ cxn.execution_log_since_checkpoint.size.should == 1
140
+ end
141
+
142
+ def should_contain_statement(cxn, entry_point)
143
+ cxn.execution_log_since_checkpoint.should \
144
+ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
145
+ end
146
+
147
+ def should_not_contain_statement(cxn, entry_point)
148
+ cxn.execution_log_since_checkpoint.should_not \
149
+ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
150
+ end
151
+
152
+ it "should tag logged statements with their entry point" do
153
+ cxn = @employee.connection
154
+
155
+ should_not_contain_statement(cxn, :insert)
156
+ @employee.save
157
+ should_contain_statement(cxn, :insert)
158
+
159
+ cxn.checkpoint!
160
+ should_not_contain_statement(cxn, :update)
161
+ @employee.save
162
+ should_contain_statement(cxn, :update)
163
+
164
+ cxn.checkpoint!
165
+ should_not_contain_statement(cxn, :delete)
166
+ @employee.destroy
167
+ should_contain_statement(cxn, :delete)
168
+
169
+ cxn.checkpoint!
170
+ should_not_contain_statement(cxn, :select_all)
171
+ Employee.find(:all)
172
+ should_contain_statement(cxn, :select_all)
173
+
174
+ cxn.checkpoint!
175
+ should_not_contain_statement(cxn, :select_value)
176
+ Employee.count_by_sql("frobozz")
177
+ should_contain_statement(cxn, :select_value)
178
+ end
179
+
180
+ it "should allow #finish to be called on the result of #execute" do
181
+ @employee.connection.execute("blah").finish
182
+ end
183
+
184
+ def should_have_column(klass, col_name, col_type)
185
+ col = klass.columns_hash[col_name.to_s]
186
+ col.should_not be_nil
187
+ col.type.should == col_type
188
+ end
189
+ end
@@ -0,0 +1,33 @@
1
+ # Sadly, we have to monkeypatch Rake because all of the Rails database tasks are
2
+ # hardcoded for specific adapters, with no extension points (!)
3
+ Rake::TaskManager.class_eval do
4
+ def remove_task(task_name)
5
+ @tasks.delete(task_name.to_s)
6
+ end
7
+ end
8
+
9
+ def remove_task(task_name)
10
+ Rake.application.remove_task(task_name)
11
+ end
12
+
13
+ def wrap_task(task_name, &wrapper)
14
+ wrapped_task = Rake::Task[task_name]
15
+ remove_task(Rake::Task.scope_name(Rake.application.current_scope,
16
+ task_name))
17
+ task(task_name) do
18
+ wrapper.call(wrapped_task)
19
+ end
20
+ end
21
+
22
+ # For later exploration...
23
+ # namespace :db do
24
+ # namespace :test do
25
+ # wrap_task :purge do |wrapped_task|
26
+ # if ActiveRecord::Base.configurations["test"]["adapter"] == "nulldb"
27
+ # # NO-OP
28
+ # else
29
+ # wrapped_task.invoke
30
+ # end
31
+ # end
32
+ # end
33
+ # end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-nulldb-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Avdi Grimm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-30 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: An ActiveRecord null database adapter for greater speed and isolation in unit tests
17
+ email: avdi@avdi.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README
25
+ files:
26
+ - LICENSE
27
+ - README
28
+ - Rakefile
29
+ - VERSION
30
+ - activerecord-nulldb-adapter.gemspec
31
+ - init.rb
32
+ - lib/active_record/connection_adapters/nulldb_adapter.rb
33
+ - lib/nulldb_rspec.rb
34
+ - spec/nulldb_spec.rb
35
+ - tasks/database.rake
36
+ has_rdoc: true
37
+ homepage: http://nulldb.rubyforge.org
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: nulldb
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: NullDB lets you to test your models without ever touching a real database.
64
+ test_files:
65
+ - spec/nulldb_spec.rb