Search code examples
ruby-on-railsrspecfactory-botrspec-rails

How to create nested items automatically for invoice in RSpec tests?


Happy New Year everyone.

In my Rails app I have invoices which can have many nested items:

class Invoice < ActiveRecord::Base

  belongs_to :user

  has_many :items

  accepts_nested_attributes_for :items, :reject_if => :all_blank, :allow_destroy => true

  ...

end

The controller looks like this:

class InvoicesController < ApplicationController

  before_action :find_invoice

  ...

  def new
    @invoice = current_user.invoices.build(:number => current_user.next_invoice_number)
    @invoice.build_item(current_user)
  end 

  def create
    @invoice = current_user.invoices.build(invoice_params)   
    if @invoice.save
      flash[:success] = "Invoice created."
      redirect_to edit_invoice_path(@invoice)
    else
      render :new
    end
  end

  ...

  private

  def find_invoice
    @invoice = Invoice.find(params[:id])
  end

end

In order to be able to create RSpec tests for this I started off by defining a factory:

FactoryGirl.define do

  factory :invoice do
    number { Random.new.rand(1..1000000) }
  end

  factory :item do
    date { Time.now.to_date }
    description "Just a test"
    price 50
    quantity 2
    tax_rate 10
  end

end

What is the best way to tell RSpec that it should always include 2 items with any invoice that gets created during a test?

I find this difficult because RSpec doesn't care about my new controller action.

What is the Rails way to do this?

Thanks for any help.


Solution

  • You could use calbacks feature of factory_girl. Here is an article on that: http://robots.thoughtbot.com/get-your-callbacks-on-with-factory-girl-3-3

    In this case following line, added to the invoice factory will probably do the job:

    after(:create) {|instance| create_list(:item, 2, invoice: instance) }
    

    And it should be used by Rspec like this:

    describe InvoicesController do
    
      describe "#new" do
        before do
          create(:invoice)
        end 
    
        # expectations here
    
      end
    
    end