unit_record 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ module UnitRecord
2
+ module DisconnectedFixtures
3
+ def disconnect!
4
+ (class << self; self; end).class_eval do
5
+ def create_fixtures(*args); end
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module UnitRecord
2
+ module DisconnectedTestCase
3
+ def disconnect!
4
+ self.use_transactional_fixtures = false
5
+ end
6
+ end
7
+ end
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + "/disconnecting_test_helper"
2
+
3
+ class DisconnectedTestCaseTest < Test::Unit::TestCase
4
+ test "create_fixtures does nothing" do
5
+ assert_nothing_raised { Fixtures.create_fixtures }
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + "/disconnecting_test_helper"
2
+
3
+ class DisconnectedTestCaseTest < Test::Unit::TestCase
4
+ test "use_transactional_fixtures is false" do
5
+ assert_equal false, Test::Unit::TestCase.use_transactional_fixtures
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + "/../test_helper" unless defined?(RAILS_ROOT)
2
+ ActiveRecord::Base.disconnect!
data/test/schema.rb ADDED
@@ -0,0 +1,6 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :people do |t|
3
+ t.column :first_name, :string
4
+ t.column :last_name, :string
5
+ end
6
+ end
@@ -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
+