I already have introduced the RoR API project and told about the models and their tests. Having this logic model set, it’s time to materialize in any way how this is going to be presented to the user. Thus we need test our controllers. In order to achieve this, we have to guarantee we have enough dummy objects to test. But as you can imagine, setting data by our own means can be difficult. We have to use Factories. Let’s check it out.
Factories
To test what’s going to happen with the controllers for real request and the real TDD, we need to set the factories for our entities.
A factory is an instance of a model holding dummy data to test. As we’ve done before, let’s start with the Producer
factory:
The Producer
Factory
Keeping on mind that the Producer
model inherits from the User
model, it’s pertinent to check the User
factory first.
1# /spec/factories/users.rb
2FactoryGirl.define do
3 factory :user do
4 username {Faker::Internet.user_name}
5 password {Faker::Internet.password(8, 20)}
6 email {Faker::Internet.email}
7 end
8end
Quick explanation. As we only need the username, the password and the email for a user, this factory will take care, precisely, of that, we only have to set the name of the factory (:user
) right after the factory
reserved word, and populate using Faker
. This utility will let you use common data for those purposes. This particular factory will serve as our base for both the Producer
and the Warehouse
factories. Now let’s review the three factories for producers:
1# /spec/factories/producers.rb
2FactoryGirl.define do
3 factory :producer, class: Producer, parent: :user do
4 first_name {Faker::Name.first_name}
5 last_name {Faker::Name.last_name}
6 end
7
8 factory :producer_with_places, class: Producer, parent: :user do |f|
9 f.first_name {Faker::Name.first_name}
10 f.last_name {Faker::Name.last_name}
11
12 transient do
13 places_count 5
14 end
15
16 f.after(:create) {|pwp, eval|
17 pwp.places << create_list(:producer_place, eval.places_count, localizable: pwp)
18 }
19 end
20
21 factory :producer_products, class: Producer, parent: :user do |f|
22 f.first_name {Faker::Name.first_name}
23 f.last_name {Faker::Name.last_name}
24
25 transient do
26 crops_count 5
27 end
28
29 f.after(:create) {|pp, eval|
30 products = create_list(:product, 10)
31 containers = create_list(:container, 10)
32 crops = Array.new([])
33 eval.crops_count.times do |i|
34 crops.push(create(:crop, producer: pp, product: products.sample, container: containers.sample))
35 end
36 pp.crops << crops
37 }
38 end
39end
Again, this factory is the result of many iterations. When I wrote the initial versions of the API, though I had a clear picture of what it was going to be, I didn’t envision the details. As you can imagine I only wrote the first factory (:producer
). The subsequent two factories were the result of walking through the different stages of the API.
What’s common for these three factories is the fact that they have been defined as having a parent (:user
). For the first factory it only completes what’s missing from the parent, in this case it adds the values for first_name
and last_name
.
The second factory (as you might guess from the factory name) creates a producer that has places. The transient value defines 5 places for a :producer_with_places
, nonetheless this value can be overwritten when needed. For the list of places the current Producer
is associated.
For the third one it’s pretty much a similar procedure.
The Place
Factory
Not a hard one, but good to show the difference between places for a producer and places for a warehouse.
1# /spec/factories/places.rb
2FactoryGirl.define do
3 factory :producer_place, class: Place do
4 tag { Faker::Address.street_name }
5 lat { Faker::Address.latitude }
6 lon { Faker::Address.longitude }
7 localizable { FactoryGirl.create(:producer) }
8 end
9
10 factory :warehouse_place, class: Place do
11 tag { Faker::Address.street_name }
12 lat { Faker::Address.latitude }
13 lon { Faker::Address.longitude }
14 localizable { FactoryGirl.create(:warehouse) }
15 end
16end
Notice how the localizable
is created depending on the type of place.
The Route
Factory
Remember that a Route
should have an origin and a destination. It doesn’t matter what type it is. The factory here defines the origin as a :producer_place
and the destination as a :warehouse_place
.
1# /spec/factories/routes.rb
2FactoryGirl.define do
3 factory :route do
4 origin { FactoryGirl.create(:producer_place) }
5 destination { FactoryGirl.create(:warehouse_place) }
6 end
7end