Using before(:context)
is considered bad practice, because it's so easy to forget that you have to clean up all database changes done in the hook yourself. Usually RSpec uses transactions and just rolls back your changes after every block. This is really convenient, because each test just gets a blank slate of the database. However, this is not possible if your before block spans a whole context.
Having before(:context)
blocks can lead to random failing of other tests - for example when the before(:context)
creates objects in the database, that the other tests expected not to exist or that interferes with another object that is tested and behaves differently when the object exists.
But sometimes, if you have to test multiple expectations after doing a very expensive operation, it can come very handy to only do the expensive operation once in a before(:context)
hook. In this case you can facilitate
Database Cleaner
Show archive.org snapshot
to do the cleanup for you manually:
before(:context) do
DatebaseCleaner.start
do_expensive_test_setup
end
after(:context) do
DatabaseCleaner.clean
end
My usecase was testing variants of Carrierwave file uploads. The file uploads were transformed to a different file format and scaled down to four variants. After writing the tests for that, the test suite was considerably slower than before. So I decided to use a before(:context)
hook to do the expensive calculation only once and then do my expectations on each variant after that:
context 'uploading a jpg file' do
# do this only once because it is very expensive to calculcate the versions
before(:context) do
DatabaseCleaner.start
described_class.enable_processing = true
picture = create(:picture, :jpg_image)
@uploader = picture.file
end
after(:context) do
described_class.enable_processing = false
# Cleanup all models and their automatically created associations like tags
DatabaseCleaner.clean
end
versions = {
'thumb' => [200, 133],
'small' => [800, 532],
'medium' => [1100, 732],
'large' => [1400, 932],
}
versions.each do |version_name, dimensions|
it "scales down #{version_name}" do
expect(@uploader.send(version_name)).to have_dimensions(*dimensions)
end
it "is stored in an unambigous folder in 'public/uploads'" do
regexp = "\/public\/uploads\/test\/picture\/file\/\\d+\/#{version_name}"
expect(@uploader.send(version_name).path).to match Regexp.new(regexp)
end
it 'is saved as webp file' do
expect(@uploader.send(version_name)).to be_format('webp')
end
it 'is writable only to the owner, readable for all and not executable' do
expect(@uploader.send(version_name)).to have_permissions(0644)
end
end
context 'the original file' do
it 'is not scaled down' do
expect(@uploader).to have_dimensions(1800, 1198)
end
it 'is not stored in the public folder but in storage' do
expect(@uploader.file.path).to match %r(\/storage\/test\/picture\/file\/\d+\/)
end
end
it "makes the image writable only to the owner, readable for all and not executable" do
expect(@uploader).to have_permissions(0644)
end
it "has the correct format" do
expect(@uploader).to be_format('JPEG')
end
end