Fix spurious rspec "photo can't be blank" failures for Carrierwave

960 views Asked by At

I have a Rails 3 app that uses rspec for testing and Carrierwave for image uploads. Right now, we have a Profile model that has several image fields, for example: photo1 and photo2.

My rspec tests will fail sporadically (usually on the CI server, but occasionally on my local machine) with an error similar to the following:

 Failure/Error: let(:profile) { create(:profile) }
  ActiveRecord::RecordInvalid:
    Validation failed: Image can't be blank

The Profile model has images sourced from Rails.root.join, for example:

IMAGE_1 ||= File.open(Rails.root.join('spec/fixtures/p1.jpg'))
IMAGE_2 ||= File.open(Rails.root.join('spec/fixtures/p2.jpg'))

FactoryGirl.define do
  factory :profile do
    ...
    photo1 IMAGE_1
    photo2 IMAGE_2
    ...
  end
end

Because these spurious failures occur sporadically and are not easily reproducible, I think the reason this is happening is that sometimes the images are not "loaded" fast enough when the tests are occuring. They usually occur on the build or create methods in the spec files, since that is when validations are tested.

How can this architecture/code be changed such that the tests pass? I've taken a look at this SO answer which proposes using a NullStorage class that implements the minimum interface for a storage provider. However, while this solution does technically fix the incorrect failures, it means that every method used on the images must be stubbed out, which is tedious and difficult to maintain. Are other people using that solution, or is there a better way?

1

There are 1 answers

1
sgbett On

If the contents of the file don't matter then this will prevent any "Validation failed: File can't be blank" errors...

FactoryGirl.define do
  factory :profile do
    ...
    photo1 File.open("/dev/null")
    photo2 File.open("/dev/null")
    ...
  end
end

If the contents do matter then maybe a read & rewind would help...

IMAGE_1 ||= File.open(Rails.root.join('spec/fixtures/p1.jpg'))
IMAGE_1.read(1) #read first byte
IMAGE_1.rewind #reset IO pointer to beginning