GithubHelp home page GithubHelp logo

fixture_factory's Introduction

FixtureFactory

FixtureFactory is an attempt to merge concepts from Rails Fixtures, Hermes Builders, and Factory Bot Factories to bridge the gap between factories and fixtures.

Fixtures

Fixtures are fast, simple, and an easy way to seed your test database with sample data. Codebases that leverage fixtures often have faster test runs than factory-based test suites. This is due to fixtures being loaded once whereas factories build models for each test. Fixtures are also officially recommended over factories by the Rails core team.

Fixtures start to become a pain when you need to test models with complex state. Unlike factories, there's no simple way to ask for a model, transform it, and start using it in one line (without using a helper method or similar).

For more information on fixtures, check out the Rails guides.

Factories

Factories make testing any model nearly painless. State differences are expressed in factory definitions through traits, callbacks, and transient attributes. Factories are often paired with data generating libraries to add an extra degree of verification to your tests.

As mentioned above, factories' biggest downside is the speed trade-off of building models for each test. There are also added compatibility concerns with factories as they are not the officially recommended way of testing Rails apps.

For more information on factories, check out FactoryBot's wiki.

Fixture Factories

Fixture Factories is not a fixture replacement. Rather, they are meant to compliment an existing suite of fixtures. When testing models with complex state, a fixture factory can:

  • Provide a simple creation syntax that supports overrides
  • Use existing fixtures as templates for fixtures factories
  • Act as an alternative to redundant fixture definitions

Definition

FixtureFactory definitions can be made by anything that includes FixtureFactory::Registry. Typically, this is a test case class. Definitions are made with the .define_factories and .fixture method:

class AccountTest < ActiveSupport::TestCase
  define_factories do
    factory(:account)
    factory(:enterprise_account, class: 'Account') do
      { plan: :enterprise }
    end
  end
end

Naming

The name of your fixture is important. It infers the class (with ActiveSupport::Inflector) you'll be using, and the fixture method you source fixtures from. Fixture with non-standard names can get around this problem 2 different ways:

  1. Define a base fixture with a simple name and extend via parent:
class RecipeTest < ActiveSupport::TestCase
  define_factories do
    factory(:recipe) # infers "Recipe" and "recipes"
    factory(:cake_recipe, parent: :recipe) do
      { name: "Cake" }
    end
  end
end
  1. Use the class and via options to specify class and fixture method:
class RecipeTest < ActiveSupport::TestCase
  define_factories do
    factory(:cake_recipe, class: "Recipe", via: :recipes) do
      { name: "Cake" }
    end
  end
end

Fixtures

The whole point of fixture factories is to complement a fixture suite. Typically, you'll want to link your factories to fixtures. This is done with the via and like options:

class UserTest < ActiveSupport::TestCase
  define_factories do
    factory(:user, like: :bob)
    factory(:admin_user, class: 'User', like: :bob, via: :users) do
      { role: :admin }
    end
  end
end

Inheritance

There are two aspects of inheritance to factories definitions. Inheritance at the registry level, and inheritance at the definition level. Registry subclasses inherit definitions from their superclass. Fixture factory definitions can specify a parent factory to inherit options from. Here's an example:

class ActiveSupport::TestCase
  define_factories do
    factory(:address)
  end
end

class AddressTest < ActiveSupport::TestCase
  define_factories do
    factory(:primary_address, parent: :address)
      { primary: true }
    end
  end
end

Sequences

Factories often don't play well with uniqueness constraints. If you need to generate unique values in your factories, consider using sequences. An auto-incrementing number is passed to every factory definition block which can be used to seed unique values.

class ArticleTest < ActiveSupport::TestCase
  define_factories do
    factory(:article) do |count| # starts at 1
      { title: "Unique Article", slug: "article-#{count}" }
    end
  end
end

Usage

FixtureFactory usage is easiest in registries that include FixtureFactory::Methods. This exposes several methods that gives your tests superpowers.

attributes_for(name, overrides = {})

Generates a hash of attributes given a factory name and an optional hash of override attributes.

class UsersControllerTest < ActionDispatch::IntegrationTest
  define_factories do
    factory(:user) do
      { email: '[email protected]', admin: false }
    end
  end
  setup do
    @user_attributes = attributes_for(:user, admin: true)
    # => { email: "[email protected]", admin: true }
  end
end

attributes_for_list(name, count, overrides = {})

Generates an array of hash attributes given a factory name, a count, and an optional hash of override attributes.

class BooksControllerTest < ActionDispatch::IntegrationTest
  define_factories do
    factory(:book) do
      { title: 'Ruby Under a Microscope' }
    end
  end
  setup do
    @book_attributes = attributes_for_list(:book, 2, title: "Why's Poignant Guide to Ruby")
    # => [{ title: "Why's Poignant Guide to Ruby" }, { title: "Why's Poignant Guide to Ruby" }]
  end
end

build(name, overrides = {})

Generates an unpersisted instance of a model given a factory name and an optional hash of override attributes.

class CourseTest < ActiveSupport::TestCase
  define_factories do
    factory(:course) do
      { name: 'Rails for Zombies' }
    end
  end
  setup do
    @course = build(:course, name: 'Ruby Monk')
    # => #<Course:0x000 name: "Ruby Monk">
  end
end

build_list(name, count, overrides = {})

Generates an array of unpersisted model instances given a factory name and an optional hash of override attributes.

class PostTest < ActiveSupport::TestCase
  define_factories do
    factory(:post) do
      { title: 'Rails 5.2' }
    end
  end
  setup do
    @post = build_list(:post, 2, title: 'Rails 6')
    # => [#<Post:0x000 id: nil, title: "Rails 6">, #<Post:0x000 id: nil, title: "Rails 6">]
  end
end

create(name, overrides = {})

Generates a persisted model instance given a factory name and an optional hash of override attributes.

class CommentTest < ActiveSupport::TestCase
  define_factories do
    factory(:comment) do
      { content: 'Hello World!', post: build(:post) }
    end
  end
  setup do
    @comment = create(:comment, post: create(:post, title: 'Wow'))
    # => #<Comment:0x000 id: 1, title: "Hello World!", post_id: 1>
  end
end

create_list(name, count, overrides = {})

Generates an array of persisted model instances given a factory name and an optional hash of override attributes.

class BlogTest < ActiveSupport::TestCase
  define_factories do
    factory(:blog) do
      { name: 'Giant Robots Smashing Into Other Giant Robots' }
    end
  end
  setup do
    @blog = create_list(:blog, 2, title: 'Riding Rails')
    # => [#<Blog:0x000 id: 1, title: "Riding Rails">, #<Blog:0x000 id: 2, title: "Riding Rails">]
  end
end

fixture_factory's People

Contributors

byroot avatar casperisfine avatar gmcgibbon avatar rafaelfranca avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.