Skip to content

Instantly share code, notes, and snippets.

@ignar
Created April 18, 2016 13:43
Show Gist options
  • Save ignar/b8d10566f47b5ac081985bf4f3c69fd2 to your computer and use it in GitHub Desktop.
Save ignar/b8d10566f47b5ac081985bf4f3c69fd2 to your computer and use it in GitHub Desktop.
require 'spec_helper'
# TODO: there are a lot of code that duplicates, move it to helpers
RSpec.describe FactoryBoy do
User = Struct.new(:name, :admin)
Building = Struct.new(:address)
before(:each) do
FactoryBoy.reset
end
describe '.register' do
it 'is empty in the beginning' do
expect(described_class.register).to be_kind_of(Hash)
expect(described_class.register).to be_empty
end
end
describe '.define_factory' do
it 'stores classes' do
described_class.define_factory(User)
expect(described_class.register).to include(User)
end
it 'saves only uniq class names' do
described_class.define_factory(User)
described_class.define_factory(User)
expect(described_class.register.length).to eq(1)
end
it 'accepts default values' do
described_class.define_factory(User) do
name 'Alex'
end
object = described_class.build(User)
expect(object.name).to eq('Alex')
end
context 'symbol instead of class' do
it 'accepts symbol' do
described_class.define_factory(:user)
object = described_class.build(:user, name: 'Alex')
expect(object.name).to eq('Alex')
end
end
# TODO: raname it
context 'given different class' do
it 'creates object' do
described_class.define_factory(:admin, class: User) do
name 'Artem'
admin true
end
object = described_class.build(:admin)
expect(object).to be_kind_of(User)
expect(object.admin).to be_truthy
expect(object.name).to eq('Artem')
end
end
end
describe '.build' do
context 'unknown factory' do
it 'raises exception' do
expect {
described_class.build(Building)
}.to raise_error(described_class::NoFactoryDefined)
end
end
context 'without arguments' do
it 'creates instance of User' do
# TODO: move to helper
described_class.define_factory(User)
expect(described_class.build(User)).to be_kind_of(User)
end
end
context 'with arguments' do
it 'creates instance with params' do
described_class.define_factory(User)
object = described_class.build(User, name: 'Alex')
expect(object.name).to eq('Alex')
end
end
end
# TODO: move to separete file
describe FactoryBoy::ClassBuilder do
context 'given class' do
it 'creates instance' do
builder = described_class.new(User)
expect(builder.build.to_s).to eq('User')
end
end
context 'given symbol' do
it 'creates instance' do
builder = described_class.new(:user)
expect(builder.build.to_s).to eq('User')
end
end
context 'given symbol with class' do
it 'creates instance' do
builder = described_class.new(:admin, class: User)
expect(builder.build.to_s).to eq('User')
end
end
end
# TODO: move to separete file
describe FactoryBoy::Builder do
describe '.initialize' do
context 'without block' do
it 'creates proper builder' do
builder = described_class.new(User)
expect(builder.factory_key).to be(User)
end
end
context 'with block' do
it 'creates proper builder' do
builder = described_class.new(User) do
name 'Alex'
end
expect(builder.factory_key).to be(User)
end
end
end
describe 'prepare' do
it 'builds hash with arguments' do
builder = described_class.new(User) do
name 'Alex'
end
builder.prepare
expect(builder.hash).to eq(name: 'Alex')
end
end
describe 'builder' do
context 'no block' do
subject { described_class.new(User) }
it 'builds instnace' do
# TODO: move prepare to before each
subject.prepare
object = subject.build
expect(object).to be_kind_of(User)
end
end
context 'with block' do
subject do
described_class.new(User) do
name 'Alex'
end
end
it 'builds instance' do
subject.prepare
object = subject.build
expect(object.name).to eq('Alex')
end
end
context 'block and arguments' do
# FIX: duplication
subject do
described_class.new(User) do
name 'Alex'
end
end
it 'builds instance' do
subject.prepare
object = subject.build(name: 'Artem')
expect(object.name).to eq('Artem')
end
end
end
end
end
class FactoryBoy
NoFactoryDefined = Class.new(StandardError)
# TODO: move to separete file
class Builder
attr_reader :factory_key, :hash, :block
def initialize(factory_key, opts = {}, &block)
@factory_key = factory_key
@block = block
@opts = opts
@hash = {}
end
# TODO: remove this method
def prepare
self.instance_eval(&block) if block
end
def method_missing(key, *args)
# TODO: raise method missing
hash[key] = args.first
end
def build(args = {})
# FIX: move hash.merge and arguments building into separated class.
# because of single responsibility
arguments = hash.merge(args)
builder = ClassBuilder.new(factory_key, @opts)
klass = builder.build
klass.new(*arguments.values)
end
end
# FIX: refactory and user some kind of strategry/builder pattern
# there are a lot of branching and conditions. And it is not the best name
# TODO: move to separete file
class ClassBuilder
attr_reader :klass
def initialize(factory_key, opts = {})
if factory_key.kind_of?(Symbol) && !opts.has_key?(:class)
@klass = eval(factory_key.to_s.capitalize)
elsif factory_key.kind_of?(Symbol) && opts.has_key?(:class)
@klass = opts[:class]
else
@klass = factory_key
end
end
def build
klass
end
end
class << self
def register
@register ||= {}
end
# TODO: this method was created just for test, and I don't like it,
# may be it should be renamed/removed
def reset
@register = {}
end
def define_factory(factory_key, opts = {}, &block)
return if register.include?(factory_key)
builder = Builder.new(factory_key, opts, &block)
builder.prepare
@register[factory_key] = builder
builder
end
def build(klass, **args)
raise NoFactoryDefined unless register.has_key?(klass)
builder = register[klass]
builder.build(args)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment