unit_record 0.4.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +14 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +137 -0
  4. data/Rakefile +63 -56
  5. data/lib/active_record/connection_adapters/unit_record_adapter.rb +97 -0
  6. data/lib/unit_record.rb +8 -1
  7. data/lib/unit_record/association_stubbing.rb +39 -0
  8. data/lib/unit_record/column_extension.rb +11 -0
  9. data/lib/unit_record/disconnected_active_record.rb +10 -32
  10. data/lib/unit_record/disconnected_test_case.rb +6 -0
  11. data/test/active_record/connection_adapters/unit_record_adapter_test.rb +103 -0
  12. data/test/db/schema.rb +9 -0
  13. data/test/sample_spec.rb +40 -0
  14. data/test/test_helper.rb +28 -4
  15. data/test/unit_record/association_stubbing_test.rb +37 -0
  16. data/test/unit_record/column_cacher_test.rb +26 -0
  17. data/test/{unit → unit_record}/column_extension_test.rb +1 -1
  18. data/test/{functional → unit_record}/column_test.rb +1 -1
  19. data/test/{functional → unit_record}/controller_test.rb +1 -1
  20. data/test/unit_record/disconnected_active_record_test.rb +52 -0
  21. data/test/{functional → unit_record}/disconnected_fixtures_test.rb +1 -1
  22. data/test/unit_record/disconnected_test_case_test.rb +19 -0
  23. data/vendor/dust-0.1.6/lib/array_extension.rb +5 -0
  24. data/vendor/dust-0.1.6/lib/definition_error.rb +20 -0
  25. data/vendor/dust-0.1.6/lib/dust.rb +8 -0
  26. data/vendor/dust-0.1.6/lib/nil_extension.rb +5 -0
  27. data/vendor/dust-0.1.6/lib/object_extension.rb +62 -0
  28. data/vendor/dust-0.1.6/lib/string_extension.rb +5 -0
  29. data/vendor/dust-0.1.6/lib/symbol_extension.rb +5 -0
  30. data/vendor/dust-0.1.6/lib/test_case_extension.rb +76 -0
  31. data/vendor/dust-0.1.6/rakefile.rb +50 -0
  32. data/vendor/dust-0.1.6/test/all_tests.rb +1 -0
  33. data/vendor/dust-0.1.6/test/failing_with_helper_unit_test.rb +16 -0
  34. data/vendor/dust-0.1.6/test/failing_with_setup_unit_test.rb +16 -0
  35. data/vendor/dust-0.1.6/test/functional_test.rb +12 -0
  36. data/vendor/dust-0.1.6/test/passing_unit_test.rb +11 -0
  37. data/vendor/dust-0.1.6/test/passing_with_helper_unit_test.rb +10 -0
  38. data/vendor/dust-0.1.6/test/passing_with_helpers_unit_test.rb +13 -0
  39. data/vendor/dust-0.1.6/test/passing_with_setup_unit_test.rb +10 -0
  40. data/vendor/dust-0.1.6/test/test_helper.rb +1 -0
  41. metadata +75 -52
  42. data/README +0 -60
  43. data/lib/unit_record/column_cacher.rb +0 -45
  44. data/test/functional/column_cacher_test.rb +0 -19
  45. data/test/functional/disconnected_active_record_test.rb +0 -33
  46. data/test/functional/disconnected_test_case_test.rb +0 -7
  47. data/test/functional/functional_test_helper.rb +0 -4
  48. data/test/unit/unit_test_helper.rb +0 -5
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ *HEAD
2
+
3
+ * Add association stubbing with mocha
4
+
5
+ ActiveRecord::Base.disconnect! :stub_associations => true
6
+
7
+ * Add option to no-op instead of raise.
8
+
9
+ ActiveRecord::Base.disconnect! :strategy => :noop
10
+ or
11
+ ActiveRecord::Base.disconnect! :strategy => :raise
12
+
13
+ * Implemented as a connection adapter
14
+
1
15
  *0.4.1* (December 10th, 2007)
2
16
 
3
17
  * Stub caching for compatibility with ActionController::Caching::SqlCache in Rails 2.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007-2008 Dan Manges
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.markdown ADDED
@@ -0,0 +1,137 @@
1
+ UnitRecord
2
+ ==========
3
+
4
+ Enables unit testing ActiveRecord classes without hitting the database.
5
+
6
+ Why?
7
+ ----
8
+
9
+ Rationale: [http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database](http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database)
10
+
11
+ One of the biggest benefits to disconnecting unit tests from the database is having a faster test suite. Here is the benchmark from one of my current projects:
12
+
13
+ Finished in 19.302702 seconds.
14
+ 4920 tests, 7878 assertions, 0 failures, 0 errors
15
+
16
+ 4 seconds per 1,000 tests is a good guideline.
17
+
18
+ Installation
19
+ ------------
20
+
21
+ gem install unit_record
22
+
23
+ Usage
24
+ -----
25
+
26
+ Restructuring the Rails Test Directory
27
+ --------------------------------------
28
+
29
+ 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. I recommend restructuring your test directory like this:
30
+
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 only needs to require <tt>test_helper.rb</tt>:
45
+
46
+ require File.dirname(__FILE__) + "/../test_helper"
47
+
48
+ 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.
49
+
50
+ Usage
51
+ -----
52
+
53
+ In the <tt>test/unit/unit\_test\_helper.rb</tt> file you created when restructuring your test directory, you should add these lines:
54
+
55
+ require File.dirname(__FILE__) + "/../test_helper"
56
+ require "unit_record"
57
+ ActiveRecord::Base.disconnect!
58
+
59
+ The <tt>disconnect!</tt> method will do everything necessary to run your unit tests without hitting the database.
60
+
61
+ Strategy
62
+ --------
63
+
64
+ There are two options for what should happen if you hit the database. You can either have UnitRecord raise an exception, or simply no-op. Raising an exception can help identify why a test is failing, but it also may be inconvenient to work around.
65
+
66
+ If you want to raise an exception:
67
+
68
+ ActiveRecord::Base.disconnect! :strategy => :raise
69
+
70
+ Person.find(:all)
71
+ #=> RuntimeError: ActiveRecord is disconnected; database access is unavailable in unit tests.
72
+
73
+ If you want to no-op:
74
+
75
+ ActiveRecord::Base.disconnect! :strategy => :noop
76
+
77
+ Person.find(:all)
78
+ #=> []
79
+
80
+ You can also change strategies within a block:
81
+
82
+ ActiveRecord::Base.connection.change_strategy(:raise) do
83
+ Person.find(:all)
84
+ #=> RuntimeError: ActiveRecord is disconnected; database access is unavailable in unit tests.
85
+ end
86
+
87
+ ActiveRecord::Base.connection.change_strategy(:noop) do
88
+ Person.find(:all)
89
+ #=> []
90
+ end
91
+
92
+ Association Stubbing
93
+ --------------------
94
+
95
+ One painful aspect of unit testing ActiveRecord classes is setting associations. Because Rails does type checking when setting an association, you'll receive an exception if you try to use a stub in place of the expected class.
96
+
97
+ Pet.new :owner => stub("person")
98
+ #=> ActiveRecord::AssociationTypeMismatch: Person(#16620740) expected, got Mocha::Mock(#11567340)
99
+
100
+ If you're using mocha, you can have UnitRecord stub associations. To enable association stubbing:
101
+
102
+ ActiveRecord::Base.disconnect! :stub_associations => true
103
+
104
+ The above example would no longer raise an exception. It would be the equivalent of:
105
+
106
+ pet = Pet.new
107
+ pet.stubs(:owner).returns(stub("person"))
108
+
109
+ Note that using this approach, the setter for the association will not work for that instance.
110
+
111
+ Development
112
+ -----------
113
+
114
+ Active development occurs on the [GitHub](http://github.com/dan-manges/unit-record). Changes are also pushed to the Rubyforge git repository.
115
+
116
+ For bugs/patches/etc please use the [Rubyforge tracker](http://rubyforge.org/tracker/?group_id=4239).
117
+
118
+ Continuous integration is provided by [RunCodeRun](http://runcoderun.com/dan-manges/unit-record).
119
+
120
+ Thanks
121
+ ------
122
+ Thanks to Jay Fields for the [original implementation](http://blog.jayfields.com/2007/03/rails-activerecord-unit-testing-part-ii.html).
123
+
124
+ Maintainer
125
+ ----------
126
+
127
+ [Dan Manges](http://www.dcmanges.com)
128
+
129
+ Contributors
130
+ ------------
131
+
132
+ * David Lowenfels
133
+ * Rob Sanheim
134
+
135
+ License
136
+ -------
137
+ Released under the MIT license
data/Rakefile CHANGED
@@ -1,87 +1,94 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
- require 'rake/rdoctask'
4
- require 'rake/gempackagetask'
5
- require 'rake/contrib/sshpublisher'
6
3
 
7
4
  desc "Default: run tests"
8
- task :default => :test
5
+ task :default => %w[test:multi_verbose spec]
9
6
 
10
- desc "Run all tests"
11
- task :test => %w[test:unit test:functional]
12
-
13
- Rake::TestTask.new("test:unit") do |t|
14
- t.libs << 'lib'
15
- t.pattern = 'test/unit/**/*_test.rb'
7
+ Rake::TestTask.new("test") do |t|
8
+ t.pattern = "test/**/*_test.rb"
16
9
  t.verbose = true
17
10
  end
18
11
 
19
- Rake::TestTask.new("test:functional") do |t|
20
- t.libs << 'lib'
21
- t.pattern = 'test/functional/**/*_test.rb'
22
- t.verbose = true
23
- end
24
-
25
- desc "Generate documentation"
26
- Rake::RDocTask.new(:rdoc) do |rdoc|
27
- rdoc.rdoc_dir = "doc"
28
- rdoc.title = "UnitRecord"
29
- rdoc.options << '--line-numbers'
30
- rdoc.rdoc_files.include('README', 'CHANGELOG')
31
- end
32
-
33
- desc "Upload RDoc to RubyForge"
34
- task :publish_rdoc => [:rdoc] do
35
- Rake::SshDirPublisher.new("dcmanges@rubyforge.org", "/var/www/gforge-projects/unit-test-ar", "doc").upload
12
+ begin
13
+ require "rcov/rcovtask"
14
+ desc "run tests with rcov"
15
+ Rcov::RcovTask.new do |t|
16
+ t.pattern = "test/**/*_test.rb"
17
+ t.rcov_opts << ["--no-html", "--exclude 'Library,#{Gem.path.join(',')}'"]
18
+ t.verbose = true
19
+ end
20
+ rescue LoadError
36
21
  end
37
22
 
38
- Gem::manage_gems
23
+ require "date"
39
24
 
40
- specification = Gem::Specification.new do |s|
25
+ gem_spec = Gem::Specification.new do |s|
41
26
  s.name = "unit_record"
42
27
  s.summary = "UnitRecord enables unit testing without hitting the database."
43
- s.version = "0.4.1"
28
+ s.version = "0.9.0"
44
29
  s.author = "Dan Manges"
45
30
  s.description = "UnitRecord enables unit testing without hitting the database."
46
31
  s.email = "daniel.manges@gmail.com"
47
32
  s.homepage = "http://unit-test-ar.rubyforge.org"
48
33
  s.rubyforge_project = "unit-test-ar"
49
-
50
- s.has_rdoc = true
51
- s.extra_rdoc_files = ['README', 'CHANGELOG']
52
- s.rdoc_options << '--title' << "UnitRecord" << '--main' << 'README' << '--line-numbers'
34
+
35
+ s.has_rdoc = false
53
36
 
54
37
  s.autorequire = "unit_record"
55
- s.files = FileList['{lib,test}/**/*.rb', 'CHANGELOG', 'README', 'Rakefile'].to_a
38
+ s.files = FileList['{lib,test,vendor}/**/*.rb', 'CHANGELOG', 'LICENSE', 'README.markdown', 'Rakefile'].to_a
56
39
  end
57
40
 
58
- Rake::GemPackageTask.new(specification) do |package|
59
- package.need_zip = false
60
- package.need_tar = false
41
+ task :gem => %w[test:multi] do
42
+ Gem::Builder.new(gem_spec).build
61
43
  end
62
44
 
63
- RUBY_VERSIONS = %w[1.8.5-p52 1.8.5-p114 1.8.6]
64
- JRUBY_VERSIONS = %w[1.1b1 1.0.2]
65
- RAILS_VERSIONS = %w[1.2.5 1.99.0 2.0.1]
45
+ namespace :gemspec do
46
+ desc "generates unit-record.gemspec"
47
+ task :generate do
48
+ File.open("unit-record.gemspec", "w") do |f|
49
+ f.puts "# this file is generated by rake gemspec:generate for github"
50
+ f.write gem_spec.to_ruby
51
+ end
52
+ end
53
+ end
54
+
55
+ task :readme do
56
+ require "rubygems"; gem "BlueCloth"; require "BlueCloth"; require 'tmpdir'
57
+ file = "#{Dir.tmpdir}/readme.html"
58
+ File.open(file, "w") { |f| f.write BlueCloth.new(File.read("README.markdown")).to_html }
59
+ sh "open #{file}"
60
+ end
61
+
62
+ RAILS_VERSIONS = %w[1.2.6 2.0.2 2.1.0 2.1.1 2.2.2]
66
63
 
67
64
  namespace :test do
68
- desc "test with multiple versions of ruby and rails"
65
+ desc "test with multiple versions of rails"
69
66
  task :multi do
70
- RUBY_VERSIONS.each do |ruby_version|
71
- bin = "/usr/local/ruby-#{ruby_version}/bin"
72
- RAILS_VERSIONS.each do |rails_version|
73
- sh "RAILS_VERSION='#{rails_version}' #{bin}/rake test > /dev/null 2>&1"
74
- end
75
- end
67
+ RAILS_VERSIONS.each do |rails_version|
68
+ puts "Testing with Rails #{rails_version}"
69
+ sh "RAILS_VERSION='#{rails_version}' rake test > /dev/null 2>&1"
70
+ end
76
71
  end
77
72
 
78
- desc "test with multiple versions of jruby and rails"
79
- task :multi_jruby do
80
- JRUBY_VERSIONS.each do |jruby_version|
81
- home = "/usr/local/jruby/jruby-#{jruby_version}"
82
- RAILS_VERSIONS.each do |rails_version|
83
- sh "JRUBY_HOME=#{home} RAILS_VERSION='#{rails_version}' #{home}/bin/rake test > /dev/null 2>&1"
84
- end
85
- end
73
+ task :multi_verbose do
74
+ (RAILS_VERSIONS - %w[2.2.2]).each do |rails_version|
75
+ task = defined?(Rcov) ? "rcov" : "test"
76
+ sh "RAILS_VERSION='#{rails_version}' rake #{task}"
77
+ end
86
78
  end
87
79
  end
80
+
81
+ begin
82
+ gem "rspec"
83
+ require "spec/rake/spectask"
84
+ Spec::Rake::SpecTask.new(:spec) do |t|
85
+ t.spec_files = %w[test/sample_spec.rb]
86
+ end
87
+ rescue LoadError
88
+ task :spec do
89
+ puts "== RSpec failed to load"
90
+ end
91
+ end
92
+
93
+ desc "pre-commit task"
94
+ task :pc => %w[test:multi spec gemspec:generate]
@@ -0,0 +1,97 @@
1
+ class ActiveRecord::ConnectionAdapters::UnitRecordAdapter < ::ActiveRecord::ConnectionAdapters::AbstractAdapter
2
+ EXCEPTION_MESSAGE = "ActiveRecord is disconnected; database access is unavailable in unit tests."
3
+
4
+ def initialize(config = {})
5
+ super
6
+ @strategy = config[:strategy] || :raise
7
+ @cached_columns = {"schema_info" => []}
8
+ end
9
+
10
+ def columns(table_name, name = nil)#:nodoc:
11
+ @cached_columns[table_name.to_s] ||
12
+ raise("Columns are not cached for '#{table_name}' - check schema.rb")
13
+ end
14
+
15
+ def create_table(table_name, options={})
16
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(self)
17
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
18
+ yield table_definition
19
+ @cached_columns[table_name.to_s] =
20
+ table_definition.columns.map do |c|
21
+ ActiveRecord::ConnectionAdapters::Column.new(c.name.to_s, c.default, c.sql_type, c.null)
22
+ end
23
+ end
24
+
25
+ def native_database_types
26
+ # Copied from the MysqlAdapter so ColumnDefinition#sql_type will work
27
+ {
28
+ :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
29
+ :string => { :name => "varchar", :limit => 255 },
30
+ :text => { :name => "text" },
31
+ :integer => { :name => "int", :limit => 11 },
32
+ :float => { :name => "float" },
33
+ :decimal => { :name => "decimal" },
34
+ :datetime => { :name => "datetime" },
35
+ :timestamp => { :name => "datetime" },
36
+ :time => { :name => "time" },
37
+ :date => { :name => "date" },
38
+ :binary => { :name => "blob" },
39
+ :boolean => { :name => "tinyint", :limit => 1 }
40
+ }
41
+ end
42
+
43
+ def change_strategy(new_strategy, &block)
44
+ unless [:noop, :raise].include?(new_strategy.to_sym)
45
+ raise ArgumentError, "#{new_strategy.inspect} is not a valid strategy - valid values are :noop and :raise"
46
+ end
47
+ begin
48
+ old_strategy = @strategy
49
+ @strategy = new_strategy.to_sym
50
+ yield
51
+ ensure
52
+ @strategy = old_strategy
53
+ end
54
+ end
55
+
56
+ def execute(sql, name = nil)
57
+ raise_or_noop
58
+ end
59
+
60
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
61
+ raise_or_noop
62
+ end if Rails::VERSION::MAJOR == 1
63
+
64
+ def select_rows(sql, name = nil)
65
+ raise_or_noop []
66
+ end
67
+
68
+ def rename_table(table_name, new_name)
69
+ raise_or_noop
70
+ end
71
+
72
+ def change_column(table_name, column_name, type, options = {})
73
+ raise_or_noop
74
+ end
75
+
76
+ def change_column_default(table_name, column_name, default)
77
+ raise_or_noop
78
+ end
79
+
80
+ def rename_column(table_name, column_name, new_column_name)
81
+ raise_or_noop
82
+ end
83
+
84
+ def tables
85
+ @cached_columns.keys
86
+ end
87
+
88
+ protected
89
+
90
+ def raise_or_noop(noop_return_value = nil)
91
+ @strategy == :raise ? raise(EXCEPTION_MESSAGE) : noop_return_value
92
+ end
93
+
94
+ def select(sql, name = nil)
95
+ raise_or_noop []
96
+ end
97
+ end
data/lib/unit_record.rb CHANGED
@@ -1,8 +1,9 @@
1
- require "unit_record/column_cacher"
1
+ require "unit_record/association_stubbing"
2
2
  require "unit_record/column_extension"
3
3
  require "unit_record/disconnected_active_record"
4
4
  require "unit_record/disconnected_test_case"
5
5
  require "unit_record/disconnected_fixtures"
6
+ require "active_record/connection_adapters/unit_record_adapter"
6
7
 
7
8
  require "active_record/fixtures"
8
9
 
@@ -10,3 +11,9 @@ ActiveRecord::ConnectionAdapters::Column.send :include, UnitRecord::ColumnExtens
10
11
  ActiveRecord::Base.extend UnitRecord::DisconnectedActiveRecord
11
12
  Test::Unit::TestCase.extend UnitRecord::DisconnectedTestCase
12
13
  Fixtures.extend UnitRecord::DisconnectedFixtures
14
+
15
+ ActiveRecord::Base.class_eval do
16
+ def self.unit_record_connection(config)
17
+ ActiveRecord::ConnectionAdapters::UnitRecordAdapter.new(config)
18
+ end
19
+ end