ruby-on-railsrspeccarrierwavefabrication-gem

Defining fabricator w/ file attachment & testing file upload w/ rspec


I'm trying to figure out the how to test file attachments w/ the Fabrication and Rspec gems. The file upload works fine when testing the site manually, there's just no Rspec coverage. The problem seems to be that I don't know how to include an attachment in a PUT request.

How do I add a file attachment, preferably using a fabricator, to this test

Fabricator:

Fabricator(:application) do
  email      Faker::Internet.email
  name       Faker::Name.name
  resume_url { File.open(
    File.join(
      Rails.root,
      "spec",
      "support",
      "files",
      "hey_look_a_pdf_pdf_lolz.pdf"
      )
    )
  }
end

Controller:

class ApplicationsController < ApplicationController
    def update
    @application = Application.find_or_initialize_by(id: params[:id])

    if @application.update(application_params)
      flash[:success] = "Application has been saved :)"
      redirect_to application_path(@application)
    else
      render :edit
    end
  end

  private

  def application_params
    params[:application].permit(:email, :job_id, :name, :resume_url)
  end
end

Controller test

require "spec_helper"

# this is a sample application attributes being passed into controller
# it should have a file attachment, but haven't figured out how to do that
#
# {
#   "id"=>"fa446fdf-b82d-48c0-8979-cbbb2de4fb47",
#   "email"=>"old@example.com",
#   "name"=>"Dr. Rebeca Dach",
#   "resume_url"=>nil,
#   "job_id"=>"cf66dbcf-d110-42cc-889b-0b5ceeeed239",
#   "created_at"=>nil,
#   "updated_at"=>nil
# }

describe ApplicationsController do
  context "PUT /applications/:id" do
    it "creates an application" do
      expect { put(
        :update,
        application: application.attributes,
        id:          application.id
      )}.to change{Application.count}.from(0).to(1)
    end
  end
end

Update #1

The following Fabricator seems to work okay. I'm still not able to get file attachments to work in controller tests.

Fabricator(:application) do
  email      Faker::Internet.email
  name       Faker::Name.name
  resume_url { ActionDispatch::Http::UploadedFile.new(
    tempfile: File.new(Rails.root.join(
      "./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf"
    )),
    filename: File.basename(File.new(Rails.root.join(
      "./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf"
    )))
  )}
end

Solution

  • OK, I got it figured out. There were two pieces.

    1) I had to fix the Fabricator, which I mentioned in "Update #1" to my question. Here's a simpler format for the Fabricator using Rack::Test::Upload.new

    Fabricator(:application) do
      email      Faker::Internet.email
      name       Faker::Name.name
      resume_url {
        Rack::Test::UploadedFile.new(
          "./spec/support/files/hey_look_a_pdf_pdf_lolz.pdf",
          "application/pdf"
        )
      }
    end
    

    2) I was using Fabricator.build(:application).attributes, which wasn't compatible with a file attachment. Instead, I started using Fabricator.attributes_for(:application), and everything started working great. Here's the passing.

    describe ApplicationsController do
      context "PUT /applications/:id" do
        let(:job) { Fabricate(:job) }
    
        let(:application) do
          Fabricate.attributes_for(
            :application,
            email:  "old@example.com",
            id:     SecureRandom.uuid,
            job_id: job.id
          )
        end
    
        it "creates an application" do
          expect { put(
            :update,
            application: application,
            id:          application["id"]
          )}.to change{Application.count}.from(0).to(1)
        end
      end
    end