Search code examples
ruby-on-railsruby-on-rails-3rspecrspec-railsrspec3

Rspec 3.8 before block not deleting record


In app/models/car.rb the class method stock looks in part like this:

 def self.stock
   raise Exception, "Property amount not set" if Property.get(:amount).nil?
   ...
 end

This Property is accessable by the user through all CRUD operations. I now want to test that if that Property is truly deleted an standard expection should be thrown. Therefore i created in my rspec model the following example group

describe '.stock' do

  describe 'if property is not set' do
    before(:all) {Property.where(name: 'amount').map(&:destroy)}
    it 'raises an exception' do
      expect{Car.stock}.to raise_error (Exception)
    end 
    after (:all) {Property.create! name: 'amount', value: 10}
  end 

  describe 'if property is set' do
    before (:all) do  
      create :car_listed
      create :car_sold
    end 
    it 'calculates the correct amount of sellable cars' do
      amount = 10 - Car.where(state: "sold")
      expect(Car.stock).to eq(amount)
    end 
  end 
end 

I make sure all Properties with that name get deleted. In the it-block i then expect the exception to be trown. After the it-block i created the property again because other tests depend on it.

In the app are properties which are not going to change during tests. So database_cleaner does not truncate the properties table. It got set through a seeds file.

config.before(:suite) do
  DatabaseCleaner.strategy = :truncation, {except: %w[properties]}
end

The test however fails with

Car
 .stock
   if property is set
    calculates the correct amount of sellable cars
   if property is not set
    raises an exception (FAILED - 1)

 Failures:

  1) Car.stock if property is not set not set raises an exception
      Failure/Error: expect{Car.stock}.to raise_error (Exception)
      expected Exception but nothing was raised
      # ./spec/models/car_spec.rb: `block (4 levels) in <top (required)>'

My question is now how do i have to delete this property properly ((:), so that my exception gets raised.


Solution

  • There are easier ways to test this that do not touch the database. Here's how to approach the problem using stubs:

    describe '.stock' do
      before do
        allow(Property).to receive(:get).with(:amount).and_return(amount)
      end
    
      context 'when property amount is NOT set' do
        let(:amount) { nil }
    
        it 'raises an exception' do
          expect { Car.stock }.to raise_error(Exception)
        end
      end
    
      context 'when amount property is set' do
        let(:amount) { 10 }
    
        before do
          create :car_listed
          create :car_sold
        end
    
        it 'does NOT raise an exception' do
          expect { Car.stock }.to_not raise_error(Exception)
        end
    
        it 'calculates the correct amount of sellable cars' do
          expect(Car.stock).to eq(amount - 1)
        end 
      end
    

    NOTE: I'm unsure about that last test since you didn't include that code.