Fake an active record model without db
Asked Answered
P

5

6

I feel like I've got to me missing something. I'm writing a ruby 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?

Paramour answered 28/9, 2011 at 16:57 Comment(0)
G
11

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.

Goldsworthy answered 28/9, 2011 at 23:20 Comment(1)
A downside to this library is how it is coupled to Rspec. Something to keep in mind if you're not using RspecKeynes
C
2

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.

Characterization answered 28/9, 2011 at 17:8 Comment(1)
you make a good point. A tiny sqlite db would be a good alternative.Paramour
G
2

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

Growth answered 28/9, 2011 at 18:9 Comment(1)
Informal looks really cool. I actually might use this for something else, thanks for pointing that out.Paramour
T
1

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
Trass answered 20/9, 2012 at 17:1 Comment(0)
N
0

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('...') }
Nicety answered 15/3, 2023 at 21:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.