How to insert predictable IDs for testing using ActiveRecord
-
21-08-2019 - |
Question
I am trying to do some Cucumber testing like the following:
Given I am logged in as admin
When I go to the article page
Then I should see "Edit Article"
And I should see "Article Title"
where the path to the article page is "articles/1" or some known id.The problem is that when I insert my data using my step definition
Article.create(:name => "Article Title")
I can't know ahead of time what the id will be when the record is inserted.
Essentially, I either need
a) to be able to insert predictable ids. I've tried the method mentioned in the answer of this question but it doesn't seem to work for me - it always inserts a different id.
or
b) write my cucumber steps in such a way that I can pass in a parameter for my Article id
All Cucumber tutorials I've seen sort of gloss over this type of scenario - it's always going to the list of all Articles or whatever, never a specific detail page.
Any help would be appreciated. Thanks!
Solution
There are two ways to create predictable ID values. The first is to manually set the ID on create:
model = Model.new(options)
model.id = DESIRED_ID
model.save
The catch here is that options passed to either new, create, or update_attributes will not affect the id value which is why the call to model.id is, unfortunately, required.
An alternative is to reset the whole table before running your tests. This can be altered to account for fixtures if required. For example:
class Model < ActiveRecord::Base
def self.reset!
connection.execute("DELETE FROM #{table_name}")
connection.execute("ALTER TABLE #{table_name} AUTO_INCREMENT=1")
end
end
This will set the ID of the first row inserted to be a predictable 1.
OTHER TIPS
How about navigating to the article page in the way a user would do it? Your step
When I go to the article page
could be changed to
When I go to the page for the "Article Title" article
and your step definition goes to an article overview, finds a link with "Article Title" and navigates to the page that way.
You get more test coverage, and don't have to worry about IDs.
How about making "Go to the Article Page" go to "/articles/#{Article.first.id}"
This is what I use:
# feature
Scenario: creator archives fragment
When fragment 1 exists
And I am the fragment owner
And I am on fragment 1
#steps
def create_fragment(attribs)
force_id = attribs[:force_id]
attribs.delete(:force_id) if force_id
fragment = Fragment.create!({ :owner_id => @current_user.id, :originator_id => @current_user.id}.merge(attribs))
if force_id
fragment.id = force_id.to_i
fragment.save!
end
fragment
end
When /^fragment (.*) exists$/ do |frag|
@fragment = Fragment.find_by_id(frag)
@fragment.destroy if @fragment
@fragment = create_fragment(:force_id => frag.to_i,:description => "test fragment #{frag}",:narrative => "narrative for fragment #{frag}")
end
# paths.rb
when /fragment (.*)/
fragment_path($1)
Have fun.