Class Factory: Factory_girl-like syntax for dynamically creating Ruby classes
Class Factory will dynamically create classes using a factories model similar to factory_girl. But instead of passing a block with model attributes into the factory definition, you pass in a migration defining the attributes of the new model class you want to create:
ClassFactory.define :person do |p| p.string :first_name p.string :last_name p.integer :age end
Now when you need a “Person” class in your tests you create one like this:
ClassFactory :person => Person(id: integer, first_name: string, last_name: string, age: integer)
This can be useful if you're writing tests for a gem or plugin and don't want to load the entire Rails environment, or have access to existing models in a target application. By default Class Factory creates ActiveRecord model classes, but using the :super option you can create any sort of Ruby class. Class Factory also makes it easy for each of your tests to use a different variation on a target class. For example, this will delete the Person model we created above, and create a new Person model that belongs to a group:
ClassFactory :person, :class_eval => 'belongs_to :group' do |p| p.string :first_name p.string :middle_name p.string :last_name p.string :group_id end => Person(id: integer, first_name: string, middle_name: string, last_name: string, group_id: string)
Creating different variations of the same class can be useful if you're writing tests for a generator, plugin or some other code which has different behavior depending on what classes you run it against.
Options
Default: create a new ActiveRecord model, along with a corresponding table in your database:
ClassFactory :person
Execute a migration on the new table specified as a block, defining the attributes of the new model class:
ClassFactory :person do |p| p.string :first_name p.string :last_name p.integer :age end
Create a class with a specified superclass (default is ActiveRecord::Base):
ClassFactory :person_array, :super => Array
If the super class is not a subclass of ActiveRecord::Base then Class Factory won't create a table or run a migration. You can use this to create plain Ruby object classes.
Create a class called “DifferentClass” instead of “Person:”
ClassFactory :person, :class => 'DifferentClass'
Run the given code inside the new class using class_eval:
ClassFactory :person, :class_eval => 'has_many :shoes'
Create a table with the given name, instead of a table called “people:”
ClassFactory :person, :class_eval => 'set_table_name :table_name', :table => 'table_name'
If you provide options when the factory is defined they will be applied to each class created with the factory. You can also provide options when you create a class, in which case they will override the factory options.
Install
gem install class_factory
Code
Detailed Example
Start an irb session and require class_factory (this will also require active_record):
$ irb > require 'rubygems' => true > require 'class_factory' => true
Create an in-memory SQLite test database:
> ActiveRecord::Base.establish_connection({ :adapter => 'sqlite3', :database => ':memory:' }) => #<ActiveRecord::ConnectionAdapters::ConnectionPool:0x19cfecc...
Define a person class factory, and create a Person class:
> ClassFactory.define :person do |p| > p.string :first_name > p.string :last_name > p.integer :age > end => #<ClassFactory:0x19c6fac @definition={:name=>:person, :migration=>... > ClassFactory :person => Person(id: integer, first_name: string, last_name: string, age: integer)
Now create an instance of a Person and count how many records we have in our test database:
> Person.create :first_name => 'Barack', :last_name => 'Obama', :age => 48 => #<Person id: 1, first_name: "Barack", last_name: "Obama", age: 48> > Person.count => 1
Next redefine the Person class and override the options set in the factory above; this time it will belong to a group. Note that the existing people table will be dropped and a new, empty people table created:
> ClassFactory :person, :class_eval => 'belongs_to :group' do |p| > p.string :first_name > p.string :middle_name > p.string :last_name > p.string :group_id > end => Person(id: integer, first_name: string, middle_name: string, last_name: string, group_id: string) > Person.count => 0
Create a group class that has many people:
> ClassFactory.define :group, :class_eval => 'has_many :people' do |g| > g.string :name > end => #<ClassFactory:0x18a05d8 @definition={:class_eval=>"has_many :people", ... > ClassFactory :group => Group(id: integer, name: string)
Finally, recreate the Barack person record and add him to the “presidents” group:
> g = Group.create :name => 'Presidents' => #<Group id: 1, name: "Presidents"> > p = Person.new :first_name => 'Barack', :last_name => 'Obama', :age => 48, :group => g => #<Person id: nil, first_name: "Barack", last_name: "Obama", group_id: 1, age: 48> > p.save => true > g.people => [#<Person id: 1, first_name: "Barack", last_name: "Obama", group_id: "1", age: 48>]