unit_record 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +69 -0
- data/Rakefile +63 -0
- data/init.rb +1 -0
- data/lib/unit_record/column_dumper.rb +52 -0
- data/lib/unit_record/disconnected_active_record.rb +28 -0
- data/lib/unit_record/disconnected_fixtures.rb +9 -0
- data/lib/unit_record/disconnected_test_case.rb +7 -0
- data/lib/unit_record.rb +8 -0
- data/test/caching/column_dumper_test.rb +20 -0
- data/test/db/columns.rb +9 -0
- data/test/disconnecting/disconnected_active_record_test.rb +16 -0
- data/test/disconnecting/disconnected_fixtures_test.rb +7 -0
- data/test/disconnecting/disconnected_test_case_test.rb +7 -0
- data/test/disconnecting/disconnecting_test_helper.rb +2 -0
- data/test/schema.rb +6 -0
- data/test/test_helper.rb +29 -0
- metadata +65 -0
data/README
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
= UnitRecord
|
2
|
+
|
3
|
+
This plugin provides unit testing for ActiveRecord by disconnecting tests from the database.
|
4
|
+
|
5
|
+
== The Benefits
|
6
|
+
|
7
|
+
Rationale: http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database
|
8
|
+
|
9
|
+
One huge benefit to disconnecting unit tests from the database is having a faster test suite. Here is the benchmark from my current ThoughtWorks project:
|
10
|
+
|
11
|
+
Finished in 11.541093 seconds.
|
12
|
+
3001 tests, 5325 assertions, 0 failures, 0 errors
|
13
|
+
|
14
|
+
== Requirements
|
15
|
+
|
16
|
+
* Rails >= 1.2
|
17
|
+
|
18
|
+
== Installation
|
19
|
+
|
20
|
+
The traditional way:
|
21
|
+
ruby script/plugin install http://unit-test-ar.rubyforge.org/svn/trunk/unit_record/
|
22
|
+
|
23
|
+
Or with piston:
|
24
|
+
piston import http://unit-test-ar.rubyforge.org/svn/trunk/unit_record/ vendor/plugins/unit_record
|
25
|
+
|
26
|
+
== Usage
|
27
|
+
|
28
|
+
=== Restructuring the Rails Test Directory
|
29
|
+
|
30
|
+
The Rails test directory typically places testing for models under <tt>test/unit</tt> and tests for controllers under <tt>test/functional</tt>. However, we need to change the definition of unit and functional. Controllers can be unit tested (mocking out models and not rendering the view). Models can be functionally tested (hitting the database). Also, each type of test needs its own test_helper. You should restructure your test directory like this:
|
31
|
+
test
|
32
|
+
test_helper.rb
|
33
|
+
unit
|
34
|
+
unit_test_helper.rb
|
35
|
+
controllers
|
36
|
+
models
|
37
|
+
functional
|
38
|
+
functional_test_helper.rb
|
39
|
+
controllers
|
40
|
+
models
|
41
|
+
|
42
|
+
You should move existing functional tests into functional/controllers. You will also need to change the require line at the top of those tests to require the functional_test_helper.rb file instead of the test_helper.rb file.
|
43
|
+
|
44
|
+
The <tt>functional_test_helper.rb</tt> file should require <tt>test_helper.rb</tt> for now:
|
45
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
46
|
+
|
47
|
+
For moving unit tests, you have a few options. I recommend moving them to unit/models and then disconnecting your unit tests from the database. Any tests that fail should then be modified to not hit the database or moved to functional/models.
|
48
|
+
|
49
|
+
== Caching Columns
|
50
|
+
|
51
|
+
The plugin disconnects models from the database by caching the column metadata. This is done through a rake task:
|
52
|
+
rake db:columns:dump
|
53
|
+
|
54
|
+
The rake task creates a db/columns.rb file which will be used in the tests. If you have a custom build/test rake task, you may just want to insert this task before you run your unit tests. Otherwise, you can automatically have it run before <tt>test:units</tt> as so:
|
55
|
+
Rake::Task["test:units"].enhance ["db:columns:dump"]
|
56
|
+
|
57
|
+
== Disconnecting
|
58
|
+
|
59
|
+
In the <tt>test/unit/unit_test_helper.rb</tt> file you created when restructuring your test directory, you should add these lines:
|
60
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
61
|
+
ActiveRecord::Base.disconnect!
|
62
|
+
|
63
|
+
The <tt>disconnect!</tt> method will do everything necessary (including loading the columns.rb file) to run your unit tests without hitting the database.
|
64
|
+
|
65
|
+
== Thanks
|
66
|
+
Thanks to Jay Fields for the original implementation:
|
67
|
+
http://blog.jayfields.com/2007/03/rails-activerecord-unit-testing-part-ii.html
|
68
|
+
|
69
|
+
Copyright (c) 2007 Dan Manges, released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/contrib/sshpublisher'
|
6
|
+
|
7
|
+
desc "Default: run tests"
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc "Run all tests"
|
11
|
+
task :test => %w[test:caching test:disconnecting]
|
12
|
+
|
13
|
+
desc "Run the tests for caching columns"
|
14
|
+
Rake::TestTask.new("test:caching") do |t|
|
15
|
+
t.libs << 'lib'
|
16
|
+
t.pattern = 'test/caching/**/*_test.rb'
|
17
|
+
t.verbose = true
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Run the tests for disconnecting"
|
21
|
+
Rake::TestTask.new("test:disconnecting") do |t|
|
22
|
+
t.libs << 'lib'
|
23
|
+
t.pattern = 'test/disconnecting/**/*_test.rb'
|
24
|
+
t.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Generate documentation"
|
28
|
+
Rake::RDocTask.new(:doc) do |rdoc|
|
29
|
+
rdoc.rdoc_dir = "doc"
|
30
|
+
rdoc.title = "UnitRecord"
|
31
|
+
rdoc.options << '--line-numbers'
|
32
|
+
rdoc.rdoc_files.include('README')
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Upload RDoc to RubyForge"
|
36
|
+
task :publish_rdoc => [:rdoc] do
|
37
|
+
Rake::SshDirPublisher.new("dcmanges@rubyforge.org", "/var/www/gforge-projects/unit-test-ar", "doc").upload
|
38
|
+
end
|
39
|
+
|
40
|
+
Gem::manage_gems
|
41
|
+
|
42
|
+
specification = Gem::Specification.new do |s|
|
43
|
+
s.name = "unit_record"
|
44
|
+
s.summary = "UnitRecord enables unit testing without hitting the database."
|
45
|
+
s.version = "0.1.0"
|
46
|
+
s.author = "Dan Manges"
|
47
|
+
s.description = "UnitRecord enables unit testing without hitting the database."
|
48
|
+
s.email = "daniel.manges@gmail.com"
|
49
|
+
s.homepage = "http://unit-test-ar.rubyforge.org"
|
50
|
+
s.rubyforge_project = "unit-test-ar"
|
51
|
+
|
52
|
+
s.has_rdoc = true
|
53
|
+
s.extra_rdoc_files = ['README']
|
54
|
+
s.rdoc_options << '--title' << "UnitRecord" << '--main' << 'README' << '--line-numbers'
|
55
|
+
|
56
|
+
s.autorequire = "unit_record"
|
57
|
+
s.files = FileList['{lib,test}/**/*.rb', '^[A-Z]+$', 'Rakefile', 'init.rb'].to_a
|
58
|
+
end
|
59
|
+
|
60
|
+
Rake::GemPackageTask.new(specification) do |package|
|
61
|
+
package.need_zip = false
|
62
|
+
package.need_tar = false
|
63
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "unit_record"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'erb'
|
2
|
+
module UnitRecord
|
3
|
+
class ColumnDumper
|
4
|
+
private_class_method :new
|
5
|
+
|
6
|
+
def self.dump(connection, stream)
|
7
|
+
new(connection).write_to(stream)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(connection)
|
11
|
+
@connection = connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def columns_by_model
|
15
|
+
@columns_by_model ||= tables.inject({}) do |hash,table|
|
16
|
+
model = model_for_table(table)
|
17
|
+
hash[model.to_s] = columns(table) if model
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def columns(table)
|
23
|
+
@connection.columns(table)
|
24
|
+
end
|
25
|
+
|
26
|
+
def model_for_table(table)
|
27
|
+
table.classify.constantize rescue nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def tables
|
31
|
+
@connection.tables
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_to(stream)
|
35
|
+
template = ERB.new <<-END, nil, '-'
|
36
|
+
<% columns_by_model.keys.sort.each do |model| -%>
|
37
|
+
<%= model %>.class_eval do
|
38
|
+
def self.columns
|
39
|
+
[
|
40
|
+
<%- columns_by_model[model].each do |column| -%>
|
41
|
+
ActiveRecord::ConnectionAdapters::Column.new(<%= column.name.to_s.inspect %>, <%= column.default.inspect %>, <%= column.sql_type.inspect %>, <%= column.null.inspect%>),
|
42
|
+
<%- end -%>
|
43
|
+
]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
<%- end -%>
|
47
|
+
END
|
48
|
+
text = template.result binding
|
49
|
+
stream.write text
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module UnitRecord
|
2
|
+
module DisconnectedActiveRecord
|
3
|
+
def disconnected?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
def disconnect!
|
7
|
+
return if disconnected?
|
8
|
+
columns_file = File.join(RAILS_ROOT, "db", "columns.rb")
|
9
|
+
File.open(columns_file, "w") do |file|
|
10
|
+
UnitRecord::ColumnDumper.dump(ActiveRecord::Base.connection, file)
|
11
|
+
end
|
12
|
+
(class << self; self; end).class_eval do
|
13
|
+
def connection
|
14
|
+
raise "ActiveRecord is disconnected; database access is unavailable in unit tests."
|
15
|
+
end
|
16
|
+
def connected?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
def disconnected?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
load columns_file
|
24
|
+
Fixtures.disconnect!
|
25
|
+
Test::Unit::TestCase.disconnect!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/unit_record.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require "unit_record/column_dumper"
|
2
|
+
require "unit_record/disconnected_active_record"
|
3
|
+
require "unit_record/disconnected_test_case"
|
4
|
+
require "unit_record/disconnected_fixtures"
|
5
|
+
ActiveRecord::Base.extend UnitRecord::DisconnectedActiveRecord
|
6
|
+
Test::Unit::TestCase.extend UnitRecord::DisconnectedTestCase
|
7
|
+
require "active_record/fixtures"
|
8
|
+
Fixtures.extend UnitRecord::DisconnectedFixtures
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
2
|
+
|
3
|
+
class ColumnDumperTest < Test::Unit::TestCase
|
4
|
+
test "dumping columns" do
|
5
|
+
stream = StringIO.new
|
6
|
+
UnitRecord::ColumnDumper.dump(ActiveRecord::Base.connection, stream)
|
7
|
+
expected = <<-END
|
8
|
+
Person.class_eval do
|
9
|
+
def self.columns
|
10
|
+
[
|
11
|
+
ActiveRecord::ConnectionAdapters::Column.new("id", nil, "INTEGER", false),
|
12
|
+
ActiveRecord::ConnectionAdapters::Column.new("first_name", nil, "varchar(255)", true),
|
13
|
+
ActiveRecord::ConnectionAdapters::Column.new("last_name", nil, "varchar(255)", true),
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
END
|
18
|
+
assert_equal expected, stream.string
|
19
|
+
end
|
20
|
+
end
|
data/test/db/columns.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Person.class_eval do
|
2
|
+
def self.columns
|
3
|
+
[
|
4
|
+
ActiveRecord::ConnectionAdapters::Column.new("id", nil, "INTEGER", false),
|
5
|
+
ActiveRecord::ConnectionAdapters::Column.new("first_name", nil, "varchar(255)", true),
|
6
|
+
ActiveRecord::ConnectionAdapters::Column.new("last_name", nil, "varchar(255)", true),
|
7
|
+
]
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/./disconnecting_test_helper"
|
2
|
+
# The '.' is intentional to have a TestCase require a different relative path.
|
3
|
+
|
4
|
+
class DisconnectedActiveRecordTest < Test::Unit::TestCase
|
5
|
+
test "accessing connection raises" do
|
6
|
+
assert_raises(RuntimeError) { ActiveRecord::Base.connection }
|
7
|
+
end
|
8
|
+
|
9
|
+
test "connected? is false" do
|
10
|
+
assert_equal false, ActiveRecord::Base.connected?
|
11
|
+
end
|
12
|
+
|
13
|
+
test "disconnected? is true" do
|
14
|
+
assert_equal true, ActiveRecord::Base.disconnected?
|
15
|
+
end
|
16
|
+
end
|
data/test/schema.rb
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
RAILS_ROOT = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'active_record'
|
7
|
+
require 'active_record/fixtures'
|
8
|
+
require 'active_support/binding_of_caller'
|
9
|
+
require 'active_support/breakpoint'
|
10
|
+
begin
|
11
|
+
require 'mocha'
|
12
|
+
require 'dust'
|
13
|
+
rescue LoadError
|
14
|
+
raise "Need Mocha and Dust gems to Test"
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Base.configurations['test'] = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
18
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
19
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'][ENV['DB'] || 'sqlite3'])
|
20
|
+
|
21
|
+
require "#{File.dirname(__FILE__)}/../init"
|
22
|
+
silence_stream(STDOUT) do
|
23
|
+
load(File.dirname(__FILE__) + "/schema.rb") if File.exist?(File.dirname(__FILE__) + "/schema.rb")
|
24
|
+
end
|
25
|
+
|
26
|
+
Test::Unit::TestCase.use_transactional_fixtures = true
|
27
|
+
|
28
|
+
class Person < ActiveRecord::Base
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.3
|
3
|
+
specification_version: 1
|
4
|
+
name: unit_record
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-08-15 00:00:00 -04:00
|
8
|
+
summary: UnitRecord enables unit testing without hitting the database.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: daniel.manges@gmail.com
|
12
|
+
homepage: http://unit-test-ar.rubyforge.org
|
13
|
+
rubyforge_project: unit-test-ar
|
14
|
+
description: UnitRecord enables unit testing without hitting the database.
|
15
|
+
autorequire: unit_record
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Dan Manges
|
31
|
+
files:
|
32
|
+
- lib/unit_record.rb
|
33
|
+
- lib/unit_record/column_dumper.rb
|
34
|
+
- lib/unit_record/disconnected_active_record.rb
|
35
|
+
- lib/unit_record/disconnected_fixtures.rb
|
36
|
+
- lib/unit_record/disconnected_test_case.rb
|
37
|
+
- test/schema.rb
|
38
|
+
- test/test_helper.rb
|
39
|
+
- test/caching/column_dumper_test.rb
|
40
|
+
- test/db/columns.rb
|
41
|
+
- test/disconnecting/disconnected_active_record_test.rb
|
42
|
+
- test/disconnecting/disconnected_fixtures_test.rb
|
43
|
+
- test/disconnecting/disconnected_test_case_test.rb
|
44
|
+
- test/disconnecting/disconnecting_test_helper.rb
|
45
|
+
- Rakefile
|
46
|
+
- init.rb
|
47
|
+
- README
|
48
|
+
test_files: []
|
49
|
+
|
50
|
+
rdoc_options:
|
51
|
+
- --title
|
52
|
+
- UnitRecord
|
53
|
+
- --main
|
54
|
+
- README
|
55
|
+
- --line-numbers
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
dependencies: []
|
65
|
+
|