I feel like I've got to me missing something. I'm writing a rub开发者_如何学Pythony gem that allows interaction with active record as an add on to its primary function.
In writing test cases for it, I need to be able to specify dummy active record models to test this functionality. It would be superb if I could get an instance of an active record model that didn't need any connection to a db, that could have relations, all that stuff, but didn't require me to setup tables in a database. I'm fairly new to testing, and outside of rails testing i'm pretty dang new, but it seems like I should be able to do something like that fairly easily, but I'm not finding anything.
Can anyone tell me what I'm missing? I've looked at factories, fabricators, fixtures, all those seem to want to hit the db. How do people test gems where you need AR object only for testing?
It sounds like you need NullDB:
NullDB is the Null Object pattern as applied to ActiveRecord database adapters. It is a database backend that translates database interactions into no-ops. Using NullDB enables you to test your model business logic - including after_save hooks - without ever touching a real database.
Others have hit this same problem. My usual take is to use a mocking library for unit tests, and write some functional ones using fixtures to complement them for setups too complex to mock (which you should avoid any way).
Or use a replacement library for AR which provides the same interface but doesn't require a DB. I haven't used rails in some time, but there used to be some available. This is not completely without the same problems as using a DB in the first place, as these libraries have other requirements usually (like web servies, LDAP, ...), or just need the same single record setup work as mocks do.
Or bite it and just use fixtures, but make their cost really small by using an in memory sqlite DB just for tests, and proper migrations.
Yeah, I wanted to do this awhile back in Rails 2.3 and it was a massive mocking headache. I think it is easier now with ActiveModel, which gives you an explicit interface, if you want to roll your own.
Also, haven't used it myself, but Josh Susser has a gem that lets you mix in AR-ish behavior into any class. It seems geared towards using plain ruby objects in forms, but it's probably useful for unit testing too. See informal.
He talks about it in a recent Ruby Rogues episode
Another option is to use a sqlite3 adapter and run the database in memory, and use a DatabaseCleaner to get rid of records after the test.
This approach have certain advantages:
- You can see the SQL in the test, that simplifies the query optimisation process
- It is close to "real life" examples
On the other hand, I should say it is a bit messy, because it is a bit long, but feel free to restructure it ;)
Here is a brief description what you need for that:
# in Gemfile
gem "activerecord" #since you are dealing with activerecord
gem "database_cleaner", :group => :test
gem "sqlite3", :group => :test
I am using the following approach to keep the thing, but you are welcome to have it differently:
# in RAILS_ROOT/test/support/active_record.rb
require 'logger'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => ':memory:'
)
#this line will print the SQL queries right into console
ActiveRecord::Base.logger = Logger.new(STDOUT)
# in RAILS_ROOT/test/support/database_cleaner.rb
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
# or DatabaseCleaner.strategy = :trunsaction (it is up to you)
module OrmSetup
def before_setup
DatabaseCleaner.start
end
def after_teardown
DatabaseCleaner.clean
end
end
# in RAILS_ROOT/test/test_helper.rb
...
require File.expand_path("support/active_record", File.dirname(__FILE__))
require File.expand_path("support/database_cleaner", File.dirname(__FILE__))
class Test::Unit::TestCase
include OrmSetup
end
And now in your test you can have something like
require 'test_helper'
class User < ActiveRecord::Base
end
class MyFancyTest < Test::Unit::TestCase
def setup
before_setup
end
def teardown
after_teardown
end
end
There is Active Entity. It supports attributes and validations. It doesn't seem to support relations the same way as AR does, however there are embeds_one
and embeds_many
methods which provide similar functionality. It seems to be a decent replacement for AR in tests without depending on a database.
I was able to use it as an ActiveRecord replacement in my rspec tests.
In RSpec tests I used it like this:
before do
stub_const('TestModel', Class.new(ActiveEntity::Base))
TestModel.attribute :my_prop, :string
end
let(:record) { TestModel.new(my_prop: 'abcdef') }
subject { MyService.call(record) }
it { is_expected.to eq('...') }
精彩评论