Cucumber transform to exclude cucumber table - ruby

I wanted a transform in my cucumber framework where whenever I give 'test test', I should get test_test in my step def.
I wrote a transform as below :
SPACE_TO_UNDERSCORE = Transform /^([^"]* [^"]*)$/ do |string|
string.tr!(" ", "_")
end
Which makes my step :
And the user selects yes to "dual nationality" on "edit user" details page
to
And(/^the user selects yes to "([^"]*)" on "([^"]*)" details page$/) do |field,page|
# field is now dual_nationality
# page is now edit_user
end
it works but the only problem is it also captures the cucumber tables steps like
below and converts the table to table:case_number string. table is Cucumber::MultilineArgument::DataTable if i don't use the transform. so obviously the transform is affecting the table if it matches its contents.
Is there a way to do that in transform where you can exclude table content ?
When the user loads a url with below case details
| case number |
| 154745 |
When(/^the user loads a url with below case details$/) do |table|
#table => "table:case_number"
end

I'd suggest not writing scenario steps like this and avoiding transforms. Your step explain 'HOW' the user says they are a dual national. It has no need to do this. Consider:
Given the user has dual nationality
Given I am a dual national
When the user confirms their dual nationality
When I confirm my dual nationality
There are of course many other ways to word this.
All these steps require no transforms, and no regex's and can easily be implemented by doing something like
find_by_css('#dual_national').set(true)
Assuming you have a dual_national checkbox.
In general every time you put detail in your scenarios about "HOW" you do things you just make live harder for yourself. There is no need to use tables, transforms or even (most of the time) regex's when using Cucumber

Related

How to use previously created objects in Gherkin?

I have a class named QuestionGroup which contains a list of Questions. supposing it has the following structure:
+QuestionGroup
-Title
And a class named Question like this:
+Question
-Title
-Description
-QuestionGroupId
And a class named InteractionGroup which contains a list of Employees:
+InteractionGroup
-Title
and a class named Employee with:
+Employee
-FirstName
-LastName
-InteractionGroupId
and I have a class named AppraisalTemplate . it contains a list of QuestionGroups and a list of InteractionGroups.
+AppraisalTemplate
-Title
-List<QuestionGroup>
-List<InteractionGroup>
now I want to write a specflow feature in which I need to create an AppraisalTemplate before my scenario runs.
my scenario depends on this AppraisalTemplate because in order to execute my When step in the scenario those steps should be processed already.
now this is the feature I have written (steps are not written yet):
Feature: Appraisals
Background:
Given I have an QuestionGroup with Title '<QuestionGroupTitle>' and following Questions
| Title | Description |
| Question 1 | Desc Test 1 |
| Question 2 | Desc Test 2 |
And an InteractionGroup with Title '<InteractionGroupTitle>' and following employees
| FirstName | LastName |
| Clubber | Lang |
| Mickey | Goldmill |
And an AppraisalTemplate with Title '<AppraisalTemplateTitle>' and following QuestionGroup and InteractionGroup
#What should I write here?
Scenario Outline: [add a new appraisal]
Given [given]
When [when]
Then [then]
I was wondering how should I write the 3rd Given step in Background section?
I want to say
Given I have created an AppraisalTemplate with the **mentioned QuestionGroup(s) and InteractionGroup(s) in the last two steps**
How can I do it?
How can I execute a step and use a previously created objects in it?
Maybe I've misunderstood the whole story. If you would please explain me if there is a mistake in my feature as well.
thank you for your time
First of all you should try and simplify you scenario to
Not use example groups
Not use scenario outlines
This will remove lots of complexity which distracts from your core question
Then you should focus on the behaviour you are trying to exercise, which seems to be about filling in an Appraisal.
Then write your Givens and Thens
So ...
When I fill in my appraisal
To do this we might have something like
Given my appraisal is due
and we would implement this with something like
Given "my appraisal is due" do
#appraisal = create_appraisal(user: #I)
end
Now this requires me to be somebody, perhaps an employee
Given I am an employee
And my appraisal is due
When I fill in my appraisal
Then ...
and we can implement that Given with
Given "I am an employee" do
#I = create_employee
end
So now we have left just the idea of creating an appraisal
module AppraisalStepHelper
def create_appraisal(user: )
...
end
end
World AppraisalStepHelper
Notice how there is nothing to do with HOW you interact with an appraisal in our feature or step definitions.
So overall we have
Feature: Employee appraisals
...
Scenario: Fill in appraisal
Given I am an employee
And my appraisal is due
When I fill in my appraisal
Then ...
Now you will probably need a load of over features to allow you to get to here. For example
Feature: Schedule An Appraisal
Feature: Create Appraisal Template
...
There are several important points to note here
cuking is about specifying WHAT you are doing and WHY its important. Its not about specifying HOW things are done
good cukes are simple and easy to read
you want to deal with one small piece of business behaviour in each feature e.g. fill in my appraisal
you simplify by
abstracting
pushing the HOW down (into step definitions and helper methods)
choosing smaller chunks
each Given builds upon a previous when
You need to change your approach to Cuke effectively. Your current approach will overwhelm you with complexity. Hopefully the stuff above helps. Good luck

Ruby Dropdown select based on first character

I have a dropdown of vehicle makes that I want my users to start selecting as they type. First character typed should find the first character in the makes. The problem is that it searches anywhere in the make for a character and does not start at the first character like my users would like. For example... if you type an "r" you get: Alfa Romeo, Aston Martin, Chevrolet, Chrysler, etc... well before you get a Renault.
I create my list from the database. My haml looks like this:
.field-row
= render partial:'/makes/make_select', locals:{id:'make_id'}
That calls this _make_select.html.haml
= collection_select :vehicle, id, Make.all.order(:name), :id, :name, {prompt:true}, {title:'Select Make', class:'make-select', 'data-allow-empty' => 'no'}
I cant seem to find any docs on Ruby that shows me the valid options for collection_select. Maybe there is an option that allows this?
I have read that I might need to use jQuery to accomplish this. Was just trying to figure out if there might be an easier way with just a simple option in the haml.
Let me know if there is anything else you would like to see.
thanks!
You could add logic to your controller and to check using a SQL query, something like this, depends on the database you're using.
#makes = Make.where('name LIKE ?', "#{params[character]}%")
Check out the MySql docs on pattern matching
https://dev.mysql.com/doc/refman/8.0/en/pattern-matching.html
You should not add your Model query inside a view, should add it.
In your above case, I suggest you use https://github.com/argerim/select2-rails it very powerful and already have what you need.

Is there anyway I can run the same test case but with different values everytime?

I have a single test case to test a particular scenario in a particular environment everyday, this is automatically done by a jenkins job.
Scenario Outline: Verify a user can book
Given I navigate to the "xxxxx" Page
And I set the "Location" field with "<location>" value
And I click on the "Search" button on "xxxxx" page
Then I verify the "Results" page is displayed
Examples:
| location |
|Boston |
I need to internally have a list of 20 locations and everytime the test case is executed it changes the location some how, can be ramdon or in any order, but always changing.
I'm using cucumber, capybara and of course ruby
Thoughts please?
Cucumber has a lot of limitations in terms of being used as a programming language. It's easier to do this kind of thing if you move it into a ruby file (cucumber files aren't ruby).
One option would be to make a single step that calls these other steps internally. Some people might say it's better to call methods rather than steps from inside other steps, but if you already have your cases written as steps than this will be quicker to do it this way, because you don't have to rewrite the code into methods. It is a good idea to write test code in methods and then call them from steps, by the way, rather than putting all the logic in the test cases.
Cucumber file:
Scenario Outline: Verify a user can book
Given I navigate to the "xxxxx" Page
Then the search bar works
Ruby file:
Then /the search bar works/ do
locations = ["Boston", "Berkeley"].shuffle
locations.each do |location|
step %{I set the "Location" field with "#{location}" value}
step %{I click on the "Search" button on "xxxxx" page}
step %{I verify the "Results" page is displayed}
end
end
Another reason this could be considered nonidiomatic is because it's packing too much into a single test case. However I'm not sure a good way to get around this other than simply copy-pasting the original step definitions in the cucumber file with different hard-coded values.
It's possible
locations = ["Boston", ...]
day_of_the_month = Date.new(2001,2,3).mday
today_location = locations[(day_of_the_month - 1) % locations.count]
I use - 1 in the third line since #mday returns integer from 1 to 31.

Using Ruby and Mechanize to make new Olympic medals count

I want to remake the Olympic medals count on London2012 to better reflect the value of the medals. Currently it is only sorted by gold medals. I'd like to relist it by points, so gold=4, silver=2 and bronze=1 to make a new more rational list. I probably want to remember the previous rank then add a new rank column as well.
I'd like to try mechanize to get raw data from site, then parse the data into rows and cols, apply the new counts, then remake the list.
From source at http://www.london2012.com/medals/medal-count/ each country has a block with medals like so:
<span class="countryName">Canada</span></a></div></div></td><td class="gold c">0</td><td class="silver c">2</td><td class="bronze c">5</td>
If I use agent.get('http://www.london2012.com/medals/medal-count') It shows the whole list. How to parse specific spans and table data?
I also need to remember the rank, then when I make the new page put the new rank beside it.
Any tips on mechanize parsing and remembering data would be really helpful. More importantly your thinking process in doing something like this, I'd appreciate the help to get me started. This doesn't have to be a code answer
Thanks
First to identify the table. In chrome load the page and right click anywhere on the table. Go to inspect element. Go up the heirarchy until you're on the table. Now select it and you'll see it looks like this:
<table class="or-tbl overall_medals sortable" summary="Schedule">
The overall_medals class looks like it will be unique so that's a good one to use. Now start irb and do:
require 'mechanize'
agent = Mechanize.new
page = agent.get 'http://www.london2012.com/medals/medal-count/'
double check that the table is unique:
page.search('table.overall_medals').size
#=> 1 (good, it is)
You can get all the data from the table into an array with:
page.search('table.overall_medals tr').map{|tr| tr.search('td').map(&:text)}
Notice that the first 2 rows are empty, let's get rid of them by using a range:
data = page.search('table.overall_medals tr')[2..-1].map{|tr| tr.search('td').map(&:text)}
The second row isn't really empty, it has the column names (in th's instead of td's). You can get those with:
columns = page.search('table.overall_medals tr[2] th').map{|th| th.text.strip}
You can get these into hashes with:
rows = data.map{|row| Hash[columns.zip row]}
Now you can do
rows[0]['Country']
#=> "United States of America"
Or even one big hash:
countries = rows.map{|row| {row['Country'] => row}}.reduce &:merge
now:
countries['France']['Gold']
#=> "8"
You might find this Medals API useful (Assuming your question is not specifically about Mechanize)
http://apify.heroku.com/resources/5014626da8cdbb0002000006
It uses Nokogiri to parse the site and the output is available as JSON:
http://apify.heroku.com/api/olympics2012_medals.json

Cucumber load data tables dynamically

I am currently trying to use cucumber together with capybara for some integration tests of a web-app.
There is one test where I just want to click through all (or most of) the pages of the web app and see if no error is returned. I want to be able to see afterwards which pages are not working.
I think that Scenario outlines would be the best approach so I started in that way:
Scenario Outline: Checking all pages pages
When I go on the page <page>
Then the page has no HTTP error response
Examples:
| page |
| "/resource1" |
| "/resource2" |
...
I currently have 82 pages and that works fine.
However I find this approach is not maintable as there may new resources and resources that will be deleted.
A better approach would be to load the data from the table from somewhere (parsing HTML of an index page, the database etc...).
But I did not figure out how to do that.
I came across an article about table transformation but I could not figure out how to use this transformation in an scenario outline.
Are there any suggestions?
OK since there is some confusion. If you have a look at the example above. All I want to do is change it so that the table is almost empty:
Scenario Outline: Checking all pages pages
When I go on the page <page>
Then the page has no HTTP error response
Examples:
| page |
| "will be generated" |
Then I want to add a transformation that looks something like this:
Transform /^table:page$/ do
all_my_pages.each do |page|
table.hashes << {:page => page}
end
table.hashes
end
I specified the transformation in the same file, but it is not executed, so I was assuming that the transformations don't work with Scenario outlines.
Cucumber is really the wrong tool for that task, you should describe functionality in terms of features. If you want to describe behavior programmatically you should use something like rspec or test-unit.
Also your scenario steps should be descriptive and specialized like a written text and not abstract phrases like used in a programming language. They should not include "incidental details" like the exact url of a ressource or it's id.
Please read http://blog.carbonfive.com/2011/11/07/modern-cucumber-and-rails-no-more-training-wheels/ and watch http://skillsmatter.com/podcast/home/refuctoring-your-cukes
Concerning your question about "inserting into tables", yes it is possible if you
mean adding additional rows to it, infact you could do anything you like with it.
The result of the Transform block completely replaces the original table.
Transform /^table:Name,Posts$/ do
# transform the table into a list of hashes
results = table.hashes.map do |row|
user = User.create! :name => row["Name"]
posts = (1..row["Posts"]).map { |i| Post.create! :title => "Nr #{i}" }
{ :user => user, :posts => posts }
end
# append another hash to the results (e.g. a User "Tim" with 2 Posts)
tim = User.create! :name => "Tim"
tims_posts = [Post.create! :title => "First", Post.create! :title => "Second"]
results << { :user => tim, :posts => tims_posts }
results
end
Given /^I have Posts of the following Users:$/ do |transformation_results|
transformation_results.each do |row|
# assing Posts to the corresponding User
row[:user].posts = row[:posts]
end
end
You could combine this with Scenario Outlines like this:
Scenario Outline: Paginate the post list of an user at 10
Given I have Posts of the following Users:
| Name | Posts |
| Max | 7 |
| Tom | 11 |
When I visit the post list of <name>
Then I should see <count> posts
Examples:
| name | count |
| Max | 7 |
| Tom | 10 |
| Tim | 2 |
This should demonstarte why "adding" rows to a table, might not be best practice.
Please note that it is impossible to expand example tags inside of a table:
Scenario Outline: Paginate the post list of an user at 10
Given I have Posts of the following Users:
| Name | Posts |
| <name> | <existing> | # won't work
When I visit the post list of <name>
Then I should see <displayed> posts
Examples:
| name | existing | displayed |
| Max | 7 | 7 |
| Tom | 11 | 10 |
| Tim | 2 | 2 |
For the specific case of loading data dynamically, here's a suggestion:
A class, let's say PageSets, with methods, e.g. all_pages_in_the_sitemap_errorcount, developing_countries_errorcount.
a step that reads something like
Given I am on the "Check Stuff" page
Then there are 0 errors in the "developing countries" pages
or
Then there are 0 errors in "all pages in the sitemap"
The Then step converts the string "developing countries" into a method name developing_countries_errorcountand attempts to call it on class PageSets. The step expects all _errorcount methods to return an integer in this case. Returning data structures like maps gives you many possibilities for writing succinct dynamic steps.
For more static data we have found YAML very useful for making our tests self-documenting and self-validating, and for helping us remove hard-to-maintain literals like "5382739" that we've all forgotten the meaning of three weeks later.
The YAML format is easy to read and can be commented if necessary (it usually isn't.)
Rather than write:
Given I am logged in as "jackrobinson#gmail.com"
And I select the "History" tab
Then I can see 5 or more "rows of history"
We can write instead:
Given I am logged in as "a user with at least 5 items of history"
When I select the "History" tab
Then I can see 5 or more "rows of history"
In file logins.yaml....
a member with at least 5 items of history:
username: jackrobinson#gmail.com
password: WalRus
We use YAML to hold sets of data relating to all sorts of entities like members, providers, policies, ... the list is growing all the time:
In file test_data.yaml...
a member who has direct debit set up:
username: jackrobinson#gmail.com
password: WalRus
policyId: 5382739
first name: Jack
last name: Robinson
partner's first name: Sally
partner's last name: Fredericks
It's also worth looking at YAML's multi-line text facilities if you need to verify text. Although that's not usual for automation tests, it can sometimes be useful.
I think that the better approach would be using different tool, just for crawling your site and checking if no error is returned. Assuming you're using Rails
The tool you might consider is: Tarantula.
https://github.com/relevance/tarantula
I hope that helps :)
A quick hack is to change the Examples collector code, and using eval of ruby to run your customized ruby function to overwrite the default collected examples data, here is the code:
generate-dynamic-examples-for-cucumber
drawback: need change the scenario_outline.rb file.

Resources