Passing parameter in "Examples:" table of "Scenario Outline:" in a feature file - ruby

Here as you can see I am trying fetch a value from .yml file located in config/environments in Examples: table.
But instead of fetching it is sending the value as it is?
Is it possible to pass parameter like this? If Yes, how?
If not, which Ruby or Cucumber feature/concept refrains user to do so and why?
Feature: Verify login of all test users
I want to verify all test users can login.
Scenario Outline: Login as different users on the website
Given I am on login page
When I enter "<username>" and password
Then I click Login button
And I see "<user>" successfully logged in
Examples:
|user|username|
|testuser1|#{FigNewton.test1_email}|
|testuser2|FigNewton.test2_email|

First of all this is a pretty poor feature, better would be
Scenario: Test Users can login
Given there are some test users
When the test users login
Then all test users should be logged in
or something like that. Features are for stating what you want to do and why, not how you do things.
IF you do the above then all the programming will be done in the step definitions. This will allow you do do whatever you want.
You can implement this quite easily e.g
Given 'there are some test users' do
#test_users = create_test_users
end
When 'the test users login' do
#login_results = login_each(#test_users)
end
Then 'all test users should be logged in' do
expect(check_for_errors(#login_results).count).to eql 0
end
then implement the methods you need in a step helper e.g
module TestUsersLoginStepHelper
def create_test_users
...
def login_each(users)
users.each do
...
...
end
World TestUsersLoginStepHelper
By putting all the work in the step definitions, you make your live much easier, as you can use the full power of ruby to do what you need

Answer to query1:
You can parametrize via Examples: table but not directly passing value using FigNewton gem because it is a .feature file not a Ruby .rb file.
Answer to query2:
How you do it:
Parametrize and Loop it on username and in you steps definition mention what to do when particular user name found. By this you can easily parametrize.
Examples:
|user|username|
|testuser1|test1|
|testuser2|test2|
Step definition
When(/^I enter "([^"]*)" and password$/) do |username|
case username
when 'test1'
on(LoginPage).user_email = FigNewton.test1_email
when 'test'
on(LoginPage).user_email = FigNewton.test2_email
end
....
....
end

You can use this DDD scenario in project whenever its needed - by using this we do not need to create multiple test cases, it will fetch data value from Example outline.
Feature file : Test case
Scenario Outline: Login to application
When I enter "username>" and "password>"
Then I click Login button
And I see user successfully logged in
Examples:
|username|password|
|abc#gmail.com|Test1234!|
|abc#yahoo.com|Test1234!|
Step definition:
When(/^I enter "([^"])" and "([^"])"$/) do |username,password|
sleep 20
on(Login).email_edit_text_element.send_keys username
on(Login).password_edit_text_element.send_keys password
end
Then(/^I click Login button$/) do
sleep 20
on(Login).login_button_element.click
end
Then(/^I see user successfully logged in$/) do
expect(on(Login).account_bg_cover_element.displayed?).to be_truthy
puts 'Login Success'
end
In ruby file, you have created methods, you are calling that methods in step definition.
It will work. make sure about the name of parameter you are passing.

Related

Mongoid Validation Error "Email Can't be blank" when testing with rspec & Capybara, but fields are not blank

I am running into an error when writing an rSpec Capybara test to mock a user signing up for the website.
It should be noted that, unfortunately, I am writing a test for a codebase that is entirely new to me, so a lot of the code for the main program is unknown to me. If asked for something I will try and dig up the relevant code, but I'm not certain what else to include at the moment. However I can say it has been successfully running in production for a while, and I can manually test it successfully - so I think the error is probably in my test, or perhaps some configuration used just while testing.
rspec test
RSpec.describe 'New User Sign Up', type: :feature do
scenario 'valid signup inputs' do
visit("/users/sign_up")
fill_in 'user_name', with: 'TEST'
fill_in 'user_email', with: 'TEST#test.com'
fill_in 'user_username_with_caps', with: "TEST"
fill_in 'user_password', with: 'TESTpw123'
puts find_field('user_email').value
puts expect(find_field('user_email').value).to eq 'TEST#test.com'
puts expect(page).to have_selector("input[value='TEST#test.com']")
puts page.should have_field('user_email', with: 'TEST#test.com')
click_on 'Create an account'
end
end
The output is:
bgc#jadzia:~/Documents/Work/BadgeList/code/badgelist/backend/spec$ bundle exec rspec sign_up_spec.rb
TEST#test.com
true
true
true
F
Failures:
1) New User Sign Up valid signup inputs
Failure/Error: click_on 'Create an account'
Mongoid::Errors::Validations:
message:
Validation of User failed.
summary:
The following errors were found: Email can't be blank
resolution:
Try persisting the document with valid data or remove the validations.
Note the puts statements and the corresponding output before the error message.
As far as I can tell, the fields ARE getting filled in properly. However, somehow this does not get recognized when it attempts to complete the sign up. The error is coming from Mongoid, so somehow mongo reacts differently to an testing auto-entered field vs. a manually entered one.
It should also be noted that, if I disable database_cleaner-mongoid, and run the same test twice... I get a -DIFFERENT- outcome. The test technically passes, but there is a warning prompt on the page that says "This email is taken".
So, somehow the value in the email field is...
Being entered/read properly when directly querying the field value on the page
Not recognized when it immediately afterwards tries to use that value to Create an account, instead the field is seen as blank.
But ALSO the field is successfully saved into the DB for a new account with this information, so running the same test again creates a conflict with the entry from the previous test if the DB is not cleaned first.
All click_on does is click on the button. It doesn't wait for the server to do anything, or anything on the page to change, etc. So ending the test with click_on isn't actually testing for any behavior, and the DB is going to get reset while the action triggered by click_on is still occurring, which is unlikely to be what you want. You need to add an expectation after the click_on testing for what you expect to see in the page like
...
click_on 'Create an account'
expect(page).to have_text('User created!!!)
Also note that the expectations you are calling puts on aren't actually defined to return anything specific, so the fact you're seeing true really is just luck. They're defined to not raise an error when successful, and raise an error when not.

unable to reference object/instance variable in separate ruby file for automation test

Ruby Newbie here! I am re-writing automated unit tests. The problem is that there are too many 'test users' that get changed and used by others. So the goal is to make a list of test users for each repository, and have them referenced in a specific file rather than hard-coding the email/password logins. I am unable to figure out how to have:
1. the .feature file with the test
2. the steps.rb with the steps for the test
3. a final user.rb file that holds class/instance variables (I don't care which I use, it just has to be able to be referenced by all the tests I will be re-writing).
I've tried referencing the email/password details from the steps.rb file in these ways:
* Then('User enters \”([^\”]*)\”$”')
* Then('User enters ')
* Then('User enters '/valid_user_email>')
I've tried defining the email/password in user.rb in these ways:
* #valid_user_email = 'user#url.com'
* puts 'valid_user_email' = 'user#url.com'
* = 'user#url.com'
Currently the login.feature file is:
Feature: Login and Logout of MySE
Scenario: Login and Logout of MySE
Given I enter the email address of an existing account
Then User enters valid_user_email in the email field
The login_steps.rb file looks like:
Given('I enter the email address of an existing account') do
visit "user/profiles"
expect(page).to have_content('Welcome')
end
Then('User enters valid_user_email in the email field') do
fill_in 'user_login', with: #valid_user_email
click_on('Next')
end
And finally, the user.rb file looks like:
class Credentials
def valid_user_email
#valid_user_email = "user#url.com"
end
def valid_user_password
puts "Testttttt01"
end
end

How do I get user input for Gherkin/Cucumber & Ruby?

I need to be able to prompt for user input (username, password and authorisation code) so my tests can access a GUI. These details cannot be stored as test data, so they'll have to be input part way through a test.
I've tried the following, but it's not working how I want:
Feature file:
Feature: user input as part of a test
Scenario: user input at the start
Given the test requires a name
Step definition:
Given(/^the test requires a name$/) do
get_a_name
end
Method:
def get_a_name
puts "Gimme a name"
#input_name = gets.chomp
puts "Hello #{#input_name}"
end
Result:
Gimme a name
Hello Feature: user input as part of a test
Any help would be much appreciated. Thanks.
You have a number of options when dealing with external services in test automation. In this particular case you can
Change your source so the authentication behaves differently when testing
OR
Record a response from the external service and use that response instead of going to the external service (see https://github.com/vcr/vcr). As the response is recorded you will know the authorisation code
OR
Use a test version of the external service which gives back know responses
I suspect there are a number of other solutions, but all three of the above are used widely and work fine.
There is certainly no need to have manual interactions to test your system unless you are running your tests against a production system (which is a very bad idea!).

Page objects in watir splash -- how to ensure that the proper page object is instantiated

I am using the Watir Splash framework to test a web application, and I have setup two page classes. The first is the "Login" page which is detailed here:
module App
module Page
class Login < WatirSplash::Page::Base
url "http://[removed].com"
def login_btn
modify button(:id => 'btnLogin'), :click => lambda {redirect_to VehicleSelection}
end
The other page class is the "Vehicle Selection" page. I have used the modify method as shown in the documentation here to ensure that the vehicle selection page object is available for RSpec after a successful login.
But what happens if the login failed? I have some test cases that deliberately feed incorrect information into the login form to ensure that the authentication is working properly. RSpec would need the methods defined in the "Login" class to access the correct elements to complete the test case. In this case, the way that I have specified the method a "VehicleSeleciton" object will be returned regardless. (or so it appears)
Any help is appreciated. Also, I'm open to other suggestions for testing frameworks, especially if there is more example code for me to reference.
Below are a couple of approaches I have tried. I was not using the WatirSplash framework, but the same concepts applied (though the attempted WatirSplash example code might not be 100% accurate).
Solution 1: Do return page objects
My personal preference is to not have page objects returning page objects. Instead, I find it easier to read/work with explicit initializations of each page object within the test. Alister Scott discussed this in his blog.
Your tests would then look like:
#For login successful tests
page = App::Page::Login.new
page.login_btn.click
page = App::Page::VehicleSelection.new #The VehicleSelection page is explicitly initialized
page.validate_page #or whatever you want to do with the page
#For login failed tests
page = App::Page::Login.new
page.login_btn.click
page.validate_page #or whatever you want to do with the page
Solution 2: Create multiple methods for login
Another solution, would be to create two login methods - one for successful login and one for unsuccessful login.
The page object could be:
module App
module Page
class Login < WatirSplash::Page::Base
url "http://[removed].com"
def login(user, password)
#Do whatever code to input name and password and then click the button
#Then redirect to the VehicleSelection page since that is where you will want to go most often
redirect_to VehicleSelection
end
def login_failed(user, password)
login(user, password)
#Return the Login page (instead of the VehicleSelection page).
redirect_to Login
end
end
end
end
With the tests being:
#For login successful tests
login_page = App::Page::Login.new
vehicle_page = login_page.login(user, password)
vehicle_page.validate_page #or whatever you want to do with the Vehicle Selection page
#For login failed tests
login_page = App::Page::Login.new
login_page.login_failed(user, password)
login_page.validate_page #or whatever you want to do with the Login page
Solution 3: Make the button know where it is going
Another solution, would be to have the login button know which page to redirect to.
The page object could be:
module App
module Page
class Login < WatirSplash::Page::Base
url "http://[removed].com"
def login_btn(login_successful=true)
if login_successful
modify button(:id => 'btnLogin'), :click => lambda {redirect_to VehicleSelection}
else
modify button(:id => 'btnLogin'), :click => lambda {redirect_to Login}
end
end
end
end
end
With the tests being:
#For login successful tests
login_page= App::Page::Login.new
vehicle_page = login_page.login_btn.click
vehicle_page.validate_page #or whatever you want to do with the Vehicle Selection page
#For login failed tests
login_page= App::Page::Login.new
login_page.login_btn(false).click
login_page.validate_page #or whatever you want to do with the Login page
Thanks for trying out my gem WatirSplash. I would have written something in the lines of solution #2 - e.g. create two separate methods for successful login and failed login. Using #modify is not needed in either method, like Justin did.
Also, i'd suggest you to use my other gem test-page instead, which is more or less the same, as Page Objects in WatirSplash, but it is extracted into separate gem - WatirSplash will be deprecated in the long term due to all of its parts being exctracted into separate gems giving better control of which functionality is needed in each project.

Reuse Cucumber steps

I want to reuse some Cucumber steps but can't seem to find the right way.
I want to write a step like:
Given /^I login with (.*) credentials$/ |type|
# do stuff with type being one of "invalid" or "valid"
end
But then have another step like:
Given /^I login successfully$
# call "Given I login with valid credentials"
end
So in testing user authentication I can use the former, but most other places, I can use the latter, and not actually have to repro code.
Is there a way to call that other step, or do I just put the logic in a helper method, and call said method from each task (basically a method extraction refactoring, which, after reading my question makes me believe that's actually the best way anyway)?
Note that the method for calling steps within steps has changed in recent versions of cucumber, which you'll see if you get an error like "WARNING: Using 'Given/When/Then' in step definitions is deprecated, use 'step' to call other steps instead:/path/to/step_definitions/foo_steps.rb:631:in `block in '
". See the cucumber wiki for details.
The gist of the change is that you should now use the step or steps methods.
When /^I make all my stuff shiny$/
step "I polish my first thing"
end
When /^I make all my stuff shiny$/
steps %Q{
When I polish my first thing
When I shine my second thing
}
end
UPDATE: The method described below has been deprecated. The recommended way to call a step from within another step now looks like this:
Given /^I login successfully$/
step "I login with valid credentials"
end
Old, deprecated method (for reference):
You can call steps from other steps like this:
Given /^I login successfully$/
Given "I login with valid credentials"
Then "I should be logged in"
end
If all of the scenarios within a feature require this (or other steps), you can also add a Background to each features, with the common steps, like so:
Background:
Given I log in with valid credentials
Scenario: Change my password
Given I am on the account page
Calling steps from step definitions is a bad practice and has some disadvantages:
If scenario will fail and there are nested step invocations, you will get only the last invoked step definition in the stack trace. It may be hard to find from which place that last stepdef was called
Call to stepdef is sometimes harder to find and read than ruby method
Ruby methods give you more power than calling steps from step defs
Aslak Hellesøy recommends to extract popular actions to World instead of reusing steps. It isolates those actions in one place, makes this code easier to find. You can extract code to usual Ruby classes or modules as well.
#/support/world_extensions.rb
module KnowsUser
def login
visit('/login')
fill_in('User name', with: user.name)
fill_in('Password', with: user.password)
click_button('Log in')
end
def user
#user ||= User.create!(:name => 'Aslak', :password => 'xyz')
end
end
World(KnowsUser)
#/step_definitions/authentication_steps.rb
When /^I login$/ do
login
end
Given /^a logged in user$/ do
login
end
Here is a useful discussion on the subject in Cucumber mailing list - link
Best wrap your steps in %{} rather than quotes. Then, you don't need to escape double quotes which you'll need to use frequently.:
Given /^I login successfully$
step %{I login with valid credentials}
end
Given /^I login with (.*) credentials$/ |type|
# do stuff with type being one of "invalid" or "valid"
end
Reuse keywords in feature file which will provide code reusability.
It is highly NOT recommended to call step defs within step defs.
I would write my feature file this way,
Scenario Outline: To check login functionality
Given I login with "<username>" and "<password>"
Then I "<may or may not>" login successfully
Examples:
|username|password|may or may not|
|paul |123$ |may |
|dave |1111 |may not |
In my step definition, (This is Java)
#Given(I login with \"([^\"]*)\" and \"([^\"]*)\"$)
public void I_login_with_and(String username, String password){
//login with username and password
}
#Then(I \"([^\"]*)\" login successfully$)
public void I_login_successully_if(String validity){
if(validity.equals("may")){
//assert for valid login
}
else
if(validity.equals("may not")){
//assert for invalid login
}
}
In this way, there is a lot of code reusability.
Your same Given and Then handles both valid and invalid scenarios.
At the same time, your feature file makes sense to the readers.

Resources