1

Here is a little simplified version of my code:

frozen_string_literal: true
RSpec.describe MyObject do
  let!(:my_object)  { create(:my_object, name: 'This Name') }
  let!(:my_ojbect_2) { create(:my_object_2, obj: my_object) }

  describe '#my_data' do
    subject { my_object.my_data }

    context 'when a' do
      ...
      ...

      it { is_expected.to eq expected_value }
    end

    context 'when b' do
      ...
      ...
      it { is_expected.to eq expected_value }
    end

    context 'when' do
      ...
      ...
      it { byebug }
    end
  end
end

When the test stopped at the byebug, I noticed that my_object was created multiple times in the database. Is there a way to have my_object only create one time?

albertski
  • 2,428
  • 1
  • 25
  • 44
  • 1
    `let` is evaluated per example by design. What you're looking to do is usually not a good idea as you're setiing yourself up for flapping tests due to residual state between tests. If you really want to do it anyways you can use `before(:all)` to setup an instance variable instead. You should also consider if you really need to save the record to the db - use `build_stubbed` instead were possible. – max Mar 13 '22 at 00:46
  • I agree with @max. You want each test case to be a clean slate. What are you trying to accomplish by making it just once? Performance? – pixelearth Mar 13 '22 at 00:53
  • Thanks. I tried `build_stubbed` but it didn't work. Also, I tried the `before(:all)`, but I ran into issues since I am using some of the variables inside the it statements. – albertski Mar 13 '22 at 00:59
  • @pixelearth I have a raw query that is something like this `( SELECT id FROM my_object WHERE my_object.name = 'This Name' LIMIT 1 )`. Since my_object is created two times, it is grabbing the wrong one. If I limited to only being created one time, my problem would go away. The problem is myobject is referenced in multiple places. – albertski Mar 13 '22 at 01:05
  • 1
    I think you don't have this set up to be cleaned between tests. You need to clean your db: https://github.com/DatabaseCleaner/database_cleaner – pixelearth Mar 13 '22 at 01:16
  • test config can get complicated with factories, fixtures, transactional fixtures, etc. I always just use the database cleaner. – pixelearth Mar 13 '22 at 01:28
  • 1
    @pixelearth Thanks the database cleaner wasn't running between tests. If you don't mind can you post that as an answer so I can give you credit since that was the correct answer? – albertski Mar 13 '22 at 01:32
  • Using `database_cleaner` has not been needed since RSpec 3.7 was released which was a long time ago. You're of course free to use it if you feel like it but in this case it really sounds like the wrong answer to the wrong question. – max Mar 14 '22 at 09:48

1 Answers1

1

If multiple copies of test objects are persisting between tests this creates huge problems for your tests, and they cannot be relied on.

You have to have a clean slate between tests. There are a number of ways to do this, but it can get complicated with factories, fixtures, transactional fixtures, etc.

I personally almost always end up using a database cleaner.

Have a look at Database Cleaner

pixelearth
  • 13,674
  • 10
  • 62
  • 110
  • Database cleaner isn't really needed in modern versions of Rails - I havent used it for years - RSpec generally works just fine with transactional fixtures. – max Mar 13 '22 at 17:06
  • I don't use fixtures personally, and often have issues with transaction-wrapped data in tests. Could be me, but I find the db cleaner useful. – pixelearth Mar 14 '22 at 01:10
  • You don't need to use fixtures, the point is really that rails gives a clean slate without needing db cleaner these days so it's less configuration. https://stackoverflow.com/q/49246124/544825 – max Mar 14 '22 at 09:43