3
\$\begingroup\$

I have a Factory Girl factory that needs to generate a unique name using Faker. Unfortunately, Factory Girl generates duplicates relatively frequently, which causes intermittent test errors. I previously added a random integer after the Faker name, but I am now checking the database to make sure that a potential name from Faker doesn't already exist.

FactoryGirl.define do
 factory :company do
 name do
 name = Faker::Company.name
 name = Faker::Company.name while Company.exists?(name: name)
 name
 end
 end
end

The above code works but seems inelegant. I'm assigning name in the first row of the block so that only one DB lookup (instead of two) will be necessary in the most common case where a Fake name is unique.

I end the block with name as otherwise the while statement returns nil when the loop ends.

asked Nov 27, 2014 at 4:32
\$\endgroup\$

4 Answers 4

8
\$\begingroup\$

The simplest thing to do is probably to use FactoryGirl's sequence:

FactoryGirl.define do
 factory :company do
 sequence(:name) { |n| "#{Faker::Company.name} #{n}" }
 end
end

sequence basically gives you an auto-incrementing integer, so you can avoid uniqueness issues. Sure, it'll generate some slightly odd company names, but for testing that shouldn't matter much.

In general though, I don't feel too great a need for something like Faker. Something like this should work just as well:

FactoryGirl.define do
 factory :company do
 sequence(:name) { |n| "Acme Inc. #{n}" }
 end
end

If it's only your specs looking at the data, it doesn't really matter if the company name's "Acme", "foobar 42", or "ACC8B1B7-7EC0-4F05-AB2A-B487C134F6BF".

Besides, your current solution is:

  1. non-deterministic, so it may never exit the while loop if it just happens to keep picking the same couple names again and again,

  2. but even if we ignore that, the act of adding more tests that use company records, will mean more name collisions and longer time spent in the loop. I don't know how Faker works, but (unlikely but technically possible) you might even end up exhausting every name Faker can come up with, and end up looping forever.

answered Nov 27, 2014 at 11:49
\$\endgroup\$
3
  • \$\begingroup\$ Sequences are good, but our challenge is that the :company factory is called as an association from several other factories at different points in our tests, so the sequences won't only be called once. Also, we can't use database transactions because Capybara is running in a different process. \$\endgroup\$ Commented Nov 27, 2014 at 15:45
  • \$\begingroup\$ @dankohn I'm not sure I understand your first point; even if a record gets built as an association when something else gets built, sequence works just fine. It's "global", so it'll be unique for each call to build/create whether you're calling it directly or not. \$\endgroup\$ Commented Nov 27, 2014 at 18:19
  • \$\begingroup\$ +1 for sequences, Faker produces random unpredictable data and can result in flaky tests that you won't be able to easily reproduce or debug. \$\endgroup\$ Commented Nov 28, 2014 at 16:42
2
\$\begingroup\$

This can be accomplished now with:

name { Faker::Company.unique.name }
answered Oct 17, 2017 at 20:26
\$\endgroup\$
0
0
\$\begingroup\$

I found a solution I like better, which replaces the while loop with a loop/break. Sequences work but look funny as fake data.

FactoryGirl.define do
 factory :company do
 name do
 loop do
 possible_name = Faker::Company.name
 break possible_name unless Company.exists?(name: possible_name)
 end
 end
 end
end
answered Nov 4, 2015 at 18:49
\$\endgroup\$
1
  • \$\begingroup\$ You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process. \$\endgroup\$ Commented May 9, 2018 at 16:51
-2
\$\begingroup\$

A different approach would be to utilize the following code:

Faker::Lorem.unique.word
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
answered May 9, 2018 at 14:47
\$\endgroup\$
2
  • \$\begingroup\$ what is Lorem? Did you mean Company? If so, this approach was already presented in dft's answer \$\endgroup\$ Commented May 9, 2018 at 15:09
  • \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please explain your reasoning (how your solution works and why it is better than the original) so that the author and other readers can learn from your thought process. \$\endgroup\$ Commented May 9, 2018 at 16:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.