0

What is the best way to see if their Active Record associations are no longer being used? How can I write a rspec test to see if the association is no longer being used?

I have to refactor someone else's (who is no longer a part of the project) Rails model. Currently, I am using atom to see if the associations are being called elsewhere in the repository, but I know this isn't the best way to do it. I want to build unit tests for the model, but am not completely sure on how to approach this for associations. Currently, I am using shoulda matchers and factory_girl_rails.

sarri xx
  • 43
  • 7
  • In a dynamically typed language like Ruby it is difficult to achieve what you are trying to do - find all places a particular method/association is being used. I am not sure if there is any trustworthy solution here. See https://stackoverflow.com/a/9788511/3507206 – tejasbubane Aug 22 '18 at 18:07

3 Answers3

1

There are at least two ways the association might be used:

  1. Explicitly - calling on an object.

    object.association_name
    

    In this case it is easy to track:

    expect_any_instance_of(Model).to_not receive(:association_name)
    

    Another question is where to place this expectation in your test structure.

  2. Implicitly - in complex queries or for associated data preloading.

    Model.all.joins(:association_name)
    Model.all.includes(:association_name)
    

    In this case it is very hard to track.

I suggest another approach:

Do not track the association usage, instead check if tests pass when the association is removed. You can safely remove the association for the test environment.

For example, you can replace this:

has_many :association_name

with this:

unless Rails.env.test?
  has_many :association_name
end

This will not break anything and will raise an error only when running tests and only if the association is being used somewhere.

chumakoff
  • 6,807
  • 2
  • 23
  • 45
  • I'm not sure I understand. How disabling it for tests only helps? Yes - you'll be "notified" when someone tries to use it in specs. But you still have no idea if it's used in the production code. And it can be used in some not covered parts, so the specs are not useful here really... or I'm missing something here. – Greg Aug 23 '18 at 14:28
  • I thought the idea was to see if the association is being used while running specs. Otherwise the `@meta`'s answer seems pretty good to me. – chumakoff Aug 23 '18 at 14:45
  • Ah! Then I get your answer. Can't say which one really OP had in mind... – Greg Aug 23 '18 at 14:56
  • thank you for your advice. I am looking to see if the associations are being used in production. I have tried using your suggestion of looking for explicit call using `expect_any_instance_of(Model).to_not receive(:association_name)`. Could you elaborate on how this works? – sarri xx Aug 23 '18 at 16:49
  • This only works when you run specs. Read this docs: https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations/expect-a-message-on-any-instance-of-a-class! – chumakoff Aug 24 '18 at 08:35
0

Rspec uses reflect_on_association for this purpose. An example would be

it 'has many associations' do
    assc = Model.reflect_on_association(:other_model)
    expect(assc.macro).to eq :has_many
end
vinyl
  • 490
  • 4
  • 8
  • This just tests that the association exists. The question is about testing if the association is actually used anywhere in the codebase. – max Aug 22 '18 at 22:39
  • Why not the simpler `is_expected.to have_many(:other_models)`? – Jagdeep Singh Aug 23 '18 at 11:43
  • this is not t at all answer to the question. The OP asks how to check that the association is not used, not how to test that it's defined. – Greg Aug 23 '18 at 14:26
0

I think this is the problem as old as the software design itself.

How to check if something is not used anymore?

It's a problem in dynamic languages like ruby. There are few ways to address that.

1. Cover all your app with end-to-end tests

Usually, it's difficult to have the 100% coverage, and this solution might not be readily available to you

2. Delete the usage and see if everything still works

Like point 1, but manual. Very difficult in bigger systems, and usually you don't have every feature documented.

3. Monkeypatch and log usages

For associations this may be sth like:

has_namy :foos

def foos
  log 'foos is used!'
  super
end

But as @chumakoff said here https://stackoverflow.com/a/51982936/299774 the association may be used in few more places. So you'd need to make some investigation to find all the places to hook your logging into.

This solution also has a drawback: how long should you wait until you declare the association unused? Should you wait half a year? What if nobody used it for that long, but there's a feature used once a year, and it got broken?

So, what do I do?

It depends on the size of the app and criticality etc. Sometimes you can risk it, sometimes you decide to live with some not used code. Or you get more creative like in here Find unused code in a Rails app.

Or you could disable this association for a fraction of users and watch for exceptions coming to wherever you aggregate logs/exceptions.

Maybe you should check the DB if there's anything in the database? If not - can you find a migration that removed all the stuff from there? Can you contact the author?

Greg
  • 5,862
  • 1
  • 25
  • 52