11 March 2009 · About 2 minutes read

Rails: Unit Testing Rails Single-Table Inheritance Models

I recently spent not an insignificant amount of time trying to get my unit tests to comply with Rails’ single-table inheritance. Single table inheritance allows you to map a hierarchy of classes to a single table in the database, so from one user table I can have an Employee and a Manager. They both inherit from a User model in Rails, and are stored in the users database table. A manager, though, might have more properties (fields) and methods than an Employee, which can be defined in manager.rb:

```ruby# user.rb

class User < ActiveRecord::Base

validates_presence_of :name

end

manager.rb

class Manager < User

has_many :employees, :foreign_key => :user_id

def grant_payrise(employee, amount = 100)

employee.pay += amount

end

end

employee.rb

class Employee < User

belongs_to :manager, :foreign_key => :user_id

def ask_for_payrise

self.manager.grant_payrise(self)

end

end```

In the code above, both employees and managers are stored in the users table from which they inherit. However, Rails will recognise which users are managers and which are employees from clever use of a reserved string field on the users table called type. The type field stores which class of user a record represents, and Rails’ finders will automatically recognise that field. This means your controller can call:

[email protected] = Employee.find(:all)

That code will only return users who are employees, and return them as Employee objects.

Unit Testing

My problem occurred when I tried to update my unit tests to cope with single-table inheritance. To keep things tidy, and separate my employee tests from my manager tests, I moved all my employee testing into test/units/employee_test.rb and my employee fixtures into test/units/employees.yml, and did the same for managers. When I ran the tests with a rake test:units, though, I was greeted with pages of SQL errors.

It turns out that you need to define all your fixtures to the root object (or direct descendent of ActiveRecord::Base), in my case the User class. So your tests for both employees and managers should still be placed in test/units/user_test.rb and the fixtures in test/units/fixtures.rb, e.g:

```ruby# users.yml

mary_manager:

first_name: Joe

last_name: Bloggs

type: Manager # Note the type field must match the ActiveRecord

joe_employee:

first_name: Joe

last_name: Bloggs

type: Employee # Note the type field must match the ActiveRecord class.

generic_user:

first_name: John

last_name: Stone

type: User```

In hindsight, this makes sense, but after some thorough research (read: Google), I couldn’t find much - if anything - on unit testing single-table inheritance models, so thought I’d post a note to help anyone else in this situation.

rails programming tips ruby unit-testing single-table-inheritance
Chris Blunt
Chris Blunt @cblunt
Chris is the founder of Plymouth Software. As well as code and business, he enjoys being a Dad, swimming, and the fine art of drinking tea.