Watch out for function shadowing when a function does not seem to be called

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the dumb-mistakes-and-gotchas category.

Last Updated: 2024-11-21

I was linting a factory (for my tests) but it kept failing the presence validation for the data attachment field. The issue must have been with the fixture method.

FactoryBot.define do
  factory :notes_file do
    released_on { 1.year.ago }
    page_count { 30 }

    data do
      fixture('competition-law.txt')
    end
  end
end

I grepped my codebase for this fixture method and found:

module GlobalTestHelpers
  def fixture(path)
    ... # get file to upload
  end
end

But this seemed to never get called, as I proved below:

def fixture(path)
  raise "never reached"
end

I started to blame the factory library FactoryBot, but this was premature. My first port of call should have been to check for method "shadowing". Another grep in my codebase revealed a second definition of fixture in another module:

module FileFixtureHelpers
  def fixture(filename)
    Rack::Test::UploadedFile.new my_fixture_path(filename)
  end
end

I.e. the real problem was that I defined a method twice without knowing about it, and one version was incorrect.

Lessons:

  1. When something isn't working, check for "shadowing" functions with grep.
  2. Better yet, get your programming language's introspection tools to tell you which version of the method was called with. E.g. In Ruby:
# Not that this may not work in certain dynamic definition styles
method(:fixture).source_location
  1. Finally, when adding new methods to a program, ESPECIALLY to a global or near global namespace, grep for existing usage. Or, alternatively, use the fully qualified method name FileFixtureHelpers.fixture for clarity.