instance_variable_set is not setting the right variable in an instance

361 Views Asked by At

I have an API endpoint I'm testing using Ruby. I have 8 tests that look like the following:

it "must rate limit on this api" do

  body = {
    ..
  }

  # Current limits assigned to this api endpoint are 5 requests every 5 minutes.
  # So this test will make 5 calls in short succession to trigger the rate limiter
  with_logging_suppressed do
    5.times do
      post '/api/endpoint/', body.to_json
      last_response.status.must_equal 200
    end

    post '/api/endpoint/', body.to_json
    last_response.status.must_equal 429
    last_response.body.include? "You are doing this too often"
  end
end

When you POST to /api/endpoint/, it spawns a RateLimit class which contains a validate_rate? method and it uses an instance variable of @timestamp which is an array of timestamps of when the API has been called.

RATE_LIMIT = RateLimit.new(5, 5.minutes)
post '/api/endpoint/' do
  if RATE_LIMIT.validate_rate?
    ...
  else
    throw Error
  end
end

RateLimit would contain the following @timestamp instance variable with the following sample of timestamps which validate_rate? would compare against.

@timestamp = [2018-04-16 19:17:48 -0400, 2018-04-16 19:17:49: -0400, 2018-04-16 19:17:58 -0400]

However, prior to each test run, I MUST clear out the @timestamp array or the array will contain timestamps from previous tests, which of course ruins the test results

So I attempted to try to set the @timestamp array to a new array prior to each test run in the form of a before block.

before do
  RateLimit.instance_variable_set(:@timestamp, [])
end

The issue I am currently running into right now is that because the RateLimit instance isn't specifically created in this test, when I call post /api/endpoint/, I cannot seem to set the @timestamp variable if this makes sense. It does set a @timestamp to [], but it's not setting the specific instance that was created when I am running the actual test

I want to keep @timestamp private, so I do not want to create a method that lets me get or set this private variable, so my only choice seems to be instance_variable_set.

1

There are 1 best solutions below

1
andriy-baran On

You should stub another method:

before do
  allow_any_instance_of(RateLimit).to receive(validate_rate?).and_return(true)
end

Then test RateLimit.new in separate test examples