Skip to content

Instantly share code, notes, and snippets.

@jni-
Last active August 29, 2015 14:00
Show Gist options
  • Select an option

  • Save jni-/11404310 to your computer and use it in GitHub Desktop.

Select an option

Save jni-/11404310 to your computer and use it in GitHub Desktop.
An attempt at implementing DB-less domain business logic in rails. Makes sense?
# inside app/models/person.rb
class Person < ActiveRecord::Base
validates :age, presence: true
include PersonModel
end
# inside lib/domain/person_model.rb
module PersonModel
def legal_age?
age > 18
end
end
# inside spec/lib/domain/person_model_spec.rb
require 'rspec'
require 'rspec_helper'
describe 'PersonModel'
let(:personModelStub) { Class.new {
include PersonModel
attr_accessor :age
}}
before(:each) do
@person_model = personModelStub.new
end
it 'should ....' do
# some test, you can use @person_model.legal_age?
end
end
@jbrains
Copy link

jbrains commented May 7, 2014

Yes, that's the typical way to do it. Unfortunately, PersonModel needs to duplicate (and stub/mock) the magic attribute readers/writers that ActiveRecord would otherwise simulate. I don't see a way around that, and it causes a leaky abstraction/unhealthy dependency, where PersonModel practically depends (though theoretically doesn't have to depend) on the database schema.

I tend to accept the risk.

@jbrains
Copy link

jbrains commented May 7, 2014

I wouldn't test Person using PersonModel. Instead, I'd test the business methods on PersonModel by stubbing/mocking the attribute readers/writers.

When I want to test Person, I'd stub/mock the methods on PersonModel.

@jni-
Copy link
Author

jni- commented May 7, 2014

Thanks @jbrains for your input!

The first concern is a pretty bad mistake on my end, I meant describe 'PersonModel'.

I too thought it would be annoying that, for each stub, I'd have to declare some attr_accessor's to mimic the active record fields. However, with time it turned out to be a really helpful documentation for the module. It clearly stated "this module needs this, this and this field/method to work". Whether or not these fields are provided by active record becomes irrelevant. Change to MongoDb and it still works!

The rule is : if it depends on an Active Record method (other than a magic field), then it goes with the AR class itself and it is tested as such. Otherwise, it goes in a module and it's unit tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment