Alfredo Ribeiro
Alfredo Ribeiro

Reputation: 68

How to test a random uniq values with rspec

I have this code:

def self.generate_random_uniq_code
  code = sprintf("%06d", SecureRandom.random_number(999999))
  code = self.generate_random_uniq_code if self.where(code: code).count > 0
  code
end

The goal is create random codes for a new register, the code can't exist already in the registers I'm trying test this way, but when I mock the SecureRandom it always return the same value:

it "code is unique" do
  old_code = Code.new
  old_code.code = 111111
  new_code = Code.new
  expect(SecureRandom).to receive(:random_number) {old_code.code}
  new_code.code = Code.generate_random_uniq_code
  expect(new_code.code).to_not eq old_code.code
end

I was trying to find if there is a way to enable and disable the mock behavior, but I could not find it, I'm not sure I'm doing the test the right way, the code seems no work fine to me. Any help is welcome, thanks!

Upvotes: 0

Views: 2882

Answers (1)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84343

TL;DR

Generally, unless you are actually testing a PRNG that you wrote, you're probably testing the wrong behavior. Consider what behavior you're actually trying to test, and examine your alternatives. In addition, a six-digit number doesn't really have enough of a key space to ensure real randomness for most purposes, so you may want to consider something more robust.

Some Alternatives

One should always test behavior, rather than implementation. Here are some alternatives to consider:

  1. Use a UUID instead of a six-digit number. The UUID is statistically less likely to encounter collisions than your current solution.
  2. Enforce uniqueness in your database column by adjusting the schema.
  3. Using a Rails uniqueness validator in your model.
  4. Use FactoryGirl sequences or lambdas to return values for your test.

Fix Your Spec

If you really insist on testing this piece of code, you should at least use the correct expectations. For example:

# This won't do anything useful, if it even runs.
expect(new_code.code).to_not  old_code.code

Instead, you should check for equality, with something like this:

old_code = 111111
new_code = Code.generate_random_uniq_code
new_code.should_not eq old_code

Your code may be broken in other ways (e.g. the code variable in your method doesn't seem to be an instance or class variable) so I won't guarantee that the above will work, but it should at least point you in the right direction.

Upvotes: 3

Related Questions