Reputation: 3523
The following code tests image validation within a model spec in a Rails 4.2 app with RSpec 3.5 and the Shrine gem for file uploads.
My questions are:
Other aspects of the file upload setup are tested in controller and feature specs, which are irrelevant to this question.
RSpec.describe ShareImage, :type => :model do
describe "#image", :focus do
let(:image_file) do
# Could not get fixture_file_upload to work, but that's irrelevant
Rack::Test::UploadedFile.new(File.join(
ActionController::TestCase.fixture_path, 'files', filename))
end
let(:share_image) { FactoryGirl.build(:share_image, image: image_file) }
before(:each) { share_image.valid? }
context "with a valid image file" do
let(:filename) { 'image-valid.jpg' }
it "attaches the image to this record" do
expect(share_image.image.metadata["filename"]).to eq filename
end
end
context "with JPG extension and 'text/plain' media type" do
let(:filename) { 'image-with-text-media-type.jpg' }
it "is invalid" do
expect(share_image.errors[:image].to_s).to include("invalid file type")
end
end
# TODO: Refactor the following test (it takes ~50 seconds to run)
context "with a >10mb image file" do
let(:filename) { 'image-11mb.jpg' }
it "is invalid" do
expect(share_image.errors[:image].to_s).to include("too large")
end
end
end
end
Upvotes: 2
Views: 3055
Reputation: 9305
I would recommend that you test metadata extraction and validation separately. The metadata extraction you need to test with real IOs, but for validation tests you can assign a cached file with the desired metadata which doesn't have to actually exist.
RSpec.describe ImageUploader do
def uploaded_file(metadata = {})
Shrine.uploaded_file(
"id" => "123",
"storage" => "cache",
"metadata" => {"mime_type" => "image/jpeg", "size" => 100}.merge(metadata)
)
end
let(:share_image) do
FactoryGirl.build(:share_image, image: uploaded_file(metadata).to_json)
end
let(:metadata) { Hash.new }
describe "validations" do
before(:each) { share_image.valid? }
context "when image is correct" do
it "passes" do
expect(share_image.errors).to be_empty
end
end
context "when extension is correct but MIME types isn't" do
let(:metadata) { Hash["filename" => "image.jpg", mime_type => "text/plain"] }
it "fails" do
expect(share_image.errors[:image].to_s).to include("isn't of allowed type")
end
end
context "when file is larger than 10MB" do
let(:metadata) { Hash["size" => 11 * 1024 * 1024] }
it "fails" do
expect(share_image.errors[:image].to_s).to include("too large")
end
end
end
end
Upvotes: 5
Reputation: 2614
Rather than routing uploads through Rack::Test::UploadedFile
, create the attachment meta-data record directly using a fixture or factory or directly in the test. The end result should be that you have the attachment meta data (which is what is constructed when you upload a file) that references your file without having to run it through the upload code. I'm not sure about the specifics of doing this with Shrine, but this technique works well with libraries like Paperclip. In Shrine it looks like this will mean constructing a Shrine::UploadedFile
record directly that references your file.
Upvotes: 1