How can I test Cucumber user input and output with Highline? - ruby

I try several ways like stubbing STDOUT, using highline-test and aruba gems, but it still stops my cucumber tests and asking for user input. Are there best practices for testing such applications with Cucumber?

Aruba gives me solution.
First of all, my mistake was creating new instance of app's root class without checking if __FILE__ == $0.
And finally it looks like this:
dashboard.feature:
Feature: Manage app with dashboard
As an app user
In order to control application
I want to have a dashboard
Scenario: View dashboard
When I run `../../lib/reporter.rb` interactively
Then I should see following choices
| 1 | Choice 1 |
| 2 | Choice 2 |
| 3 | Choice 3 |
steps.rb:
Then(/^I should see following choices$/) do |table|
menu = ''
table.rows_hash.each do |key, value|
menu << "#{key}. #{value}\n"
end
Timeout::timeout(exit_timeout) do
loop do
break if assert_partial_output_interactive(menu)
sleep 0.1
end
end
end

Related

Logging to STDOUT in a ruby program (not working in Docker)

I'm dockerizing one of my ruby apps, but I've got this very strange logging behavior. It only seems to load when the program ENDS not while it's running. When I run the program (daemon) with docker-compose all I see is this:
Starting custom_daemon_1
Attaching to custom_daemon_1
However, if I put an exit part way into the program I see all my puts and logger outputs.
Starting custom_daemon_1
Attaching to custom_daemon_1
custom_daemon_1 | requires
custom_daemon_1 | starting logger
custom_daemon_1 | Starting loads
custom_daemon_1 | Hello base
custom_daemon_1 | Loaded track
custom_daemon_1 | Loaded geo
custom_daemon_1 | Loaded geo_all
custom_daemon_1 | Loaded unique
custom_daemon_1 | D, [2016-11-14T13:31:19.295785 #1] DEBUG -- : Starting custom_daemon...
custom_daemon_1 | D, [2016-11-14T13:31:19.295889 #1] DEBUG -- : Loading xx from disk...
custom_daemon_1 exited with code 0
The top ones without times were just puts debugging, seeing if it would show - the bottom two are created by:
Logger.new(STDOUT)
LOG = Logger.new(STDOUT)
LOG.level = Logger::DEBUG
Then I would call LOG.debug "xxx" or LOG.error "xxx" any idea why this strange behavior is happening? When I ctrl+c out of the first one, the logs still do not show up.
This was originally run by a .sh script and now I've made the call to run it directly as the CMD of the Dockerfile.
There is a python question I found asking something similar here. Someone speculates it may have to do with PID 1 processes having logging to STDOUT surpressed.
Test
Here is a test I ran:
puts "starting logger"
Logger.new(STDOUT)
LOG = Logger.new(STDOUT)
LOG.level = Logger::DEBUG
puts "this is 'puts'"
p "this is 'p'"
LOG.debug "this is 'log.debug'"
puts "Starting loads"
outputs:
custom_daemon_1 | starting logger
custom_daemon_1 | this is 'puts'
custom_daemon_1 | "this is 'p'"
Notice that the first two puts printed but as soon as I try to use LOG.debug it didn't work.
TEST 2
I also decided to try the logger using a file, and as expected it logs to the file just fine, through docker.
All I did was change Logger.new(STDOUT) to Logger.new('mylog.log') and I can tail -f mylog.log and all the LOG.debug prompts show up.
As say in this thread Log issue in Rails4 with Docker running rake task
Try disabling output buffering to STDOUT: $stdout.sync = true
I've temporarily fixed this with adding a symlink to based on this docker thread. In the Dockerfile:
RUN ln -sf /proc/1/fd/1 /var/log/mylog.log and set my logger to, LOG = Logger.new('/var/log/mylog.log') but this has two undesired consequences. First, the log file will grow and take up space and probably need to be managed - I don't want to deal with that. Second, it seems inelegant to have to add a symlink to get logging to work properly... Would love another solution.

For loop to check for a xpath in Robot Framework

I am a newbie in Robot Framework. I want to implement a For loop to check for a xpath on the page. It should wait for 10 seconds for the xpath, if the xpath is still not there then wait again for 10 seconds, if the xpath has appeared then exit the loop and move ahead. A total of 10 iterations are required to wait for the element.
I am trying the below:
|| ${count}= | Get Matching Xpath Count | xpath=//*[.='Continue'] |
|| :FOR | ${loopIndex} | IN RANGE | 10
| Wait Until Page Contains Element | xpath=//*[.='Continue'] | 10
| Exit For Loop If | ${count}>0
|| Log | Got out of loop
I am getting the error right now as:
FAIL : Element 'xpath=//*[.='Continue']' did not appear in 10 seconds.
Let me know if I have missed some information. I am using RIDE for editing.
EDIT:
I have to do it in a loop. I am asking for help regarding this so that I can use this loop example in other places of my work.
You can achieve this either with a FOR loop or with a "Wait until keyword succeeds".
See both examples:
*** Test Cases ***
test with for loop
:FOR ${loopIndex} IN RANGE 10
\ ${element_is_here} = Run Keyword and return status Page should contain element xpath=//*[.='Continue']
\ Exit For Loop If ${element_is_here}
\ sleep 10s
Log I found the element
test with WUKS
wait until keyword succeeds 10s 10s Page should contain element xpath=//*[.='Continue']
Log I found the element
The keyword is failing, so the test case is failing. What you need to do instead is either not use a loop and simply wait for 100 seconds, or use something like Run keyword and ignore error or Run keyword and continue on failure (eg: Run keyword and ignore error | Wait until page contains element | ...)

Using Rake to submit a Cucumber test in Jenkins

I seem to be having some issues with Rake and my Cucumber features file.
Here is the command in Jenkins. It specifys the Rake file that I have put directly in the ./features directory so the testjson.feature is right there.
/usr/local/bin/rake --rakefile /home/robm/code/BDD/practise-tests/testtq/features/Rakefile
Rakefile looks like this:
require 'cucumber/rake/task'
Cucumber::Rake::Task.new :features do |t|
t.cucumber_opts = '*.feature'
end
It's pretty straightforward but in the console output from Jenkins I get:
Feature: Validate DUT JSON
JSON should be evaluated for all routes in API
All API routes should return valid JSON
If JSON is invalid for one or more route in API it has DUT failed
Scenario Outline: Validate JSON # testJson.feature:6
Given there is a DUT with "<input>" and "<un>" and "<pw>" # testJson.feature:7
When the JsonTest code is run # testJson.feature:8
Then the output should be "<output>" # testJson.feature:9
Examples:
| input | un | pw | output |
| 172.168.101.139 | username | password | CHECK |
| 172.168.101.214 | username | password | CHECK |
2 scenarios (2 undefined)
6 steps (6 undefined)
0m0.007s
which tells me it's not finding the feature file, right?
When I go to the directory above the feature file and run Cucumber the test works!
So, obviously I have an error with my Rake file. Any ideas?
I put this in as my Rake working directory
/home/robm/code/BDD/practise-tests/testtq
then specified the feature file
"./features/testJson.feature"
that got it to work not sure why the other didn't work.

Using After hooks for each tag specified

I am trying to get to grips with Cucumbers After Hooks and would like to run a hook after every tag in a feature. So for example within my feature login.feature I have multiple scenarios, each with their own tag
Feature: Login to myApp with various Users
#login_user_1
Scenario: Load Login Screen for User_1
Given I am on the login page
Then I will enter my credentials
Then I will successfully Login
Then I should Logout
#login_user_2
Scenario: Load Login Screen for User_2
Given I am on the login page
Then I will enter my credentials
Then I will successfully Login
Then I should Logout
My after hook consists of creating a screenshot for each scenario that fails, but at the moment only the last scenario in my feature is outputting a failure in the console, though images for both failed scenarios are created
After do |scenario|
dir_path = "/var/apps/MyApp/report/screenshots"
#Check scenario has failed?
if(scenario.failed?)
time = Time.now.strftime('%Y_%m_%d_%Y_%H_%M_%S_')
name_of_scenario = time + scenario.name.gsub(/\s+/, "_").gsub("/","_")
puts "#===========================================================#"
puts "TEST FAILED - Name of screenshot is #{name_of_scenario}"
puts "#===========================================================#"
file_path = File.expand_path(dir_path)+'/'+name_of_scenario +'.png'
page.driver.browser.save_screenshot file_path
end
end
The output i get at the moment is
Feature: Login to MyApp as various Users
#login_user_1
Scenario: Load Login Screen for user_1 # features/login.feature:4
Given I am on the login page # features/step_definitions/login.rb:1
expected to find css "#lnlogop" but there were no matches (RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/login.rb:3:in `/^I am on the login page$/'
features/login.feature:5:in `Given I am on the login page'
Then I will enter my credentials # features/step_definitions/login.rb:6
Then I will successfully Login # features/step_definitions/login.rb:13
Then I should Logout # features/step_definitions/login.rb:17
#login_user_2
Scenario: Load Login Screen for user_2 # features/login.feature:11
Given I am on the login page # features/step_definitions/login.rb:1
#===========================================================#
TEST FAILED - Name of screenshot is 2015_02_13_2015_11_15_02_Load_Login_Screen_for_MAuser
#===========================================================#
expected to find css "#lnlogop" but there were no matches (RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/login.rb:3:in `/^I am on the login page$/'
features/login.feature:12:in `Given I am on the login page'
Then I will enter my credentials # features/step_definitions/login.rb:6
Then I will successfully Login # features/step_definitions/login.rb:13
Then I should Logout # features/step_definitions/login.rb:17
Failing Scenarios:
cucumber features/login.feature:4 # Scenario: Load Login Screen for user_1
cucumber features/login.feature:11 # Scenario: Load Login Screen for user_2
What I want to achieve is have output in the console so that I know which scenario has failed.
Around Hooks
I looked into Around Hooks, and found that i could achieve this but no screenshot was being produced
Around('#user_1', '#user_2') do |scenario, block|
block.call
#Check scenario has failed?
if(scenario.failed?)
dir_path = "/var/apps/MyApp/report/screenshots"
time = Time.now.strftime('%Y_%m_%d_%Y_%H_%M_%S_')
name_of_scenario = time + scenario.name.gsub(/\s+/, "_").gsub("/","_")
puts "#===========================================================#"
puts "TEST FAILED - Name of screenshot is #{name_of_scenario}"
puts "#===========================================================#"
file_path = File.expand_path(dir_path)+'/'+name_of_scenario +'.png'
page.driver.browser.save_screenshot file_path
end
end
How can i output a failure message per scenario and get it to create the file
Thanks
Cucumber takes control of all puts messages so it can pass it along to all the formatters.
In the After hook try STDOUT.puts instead

Question about Cucumber/Pickle

I'm trying to get a little more familiarity with Rails / ActiveRecord. In trying to do so I am attempting to use Cucumber to help with some "discovery" tests.
I have the following
Feature: Describing a task
In order to work with tasks
As a user
I want to be able to know what makes up a task and to improve my understanding of ActiveRecord
Scenario: A task has certain core fields
Given the following tasks exist
| id | name |
| 1 | some task |
And the following estimates exist
| task_id | hours | explanation |
| 1 | 8 | initial estimate |
| 1 | 6 | better undertsanding of task |
| 1 | 16 | no control over inputs to create task |
| 2 | 22 | for other task |
Then a task: "task" should exist with name: "some task" #this works
Then the estimate "estimate" should exist with explanation: "initial estimate" #this works
Then the estimate "estimate" should be one of task: "task"'s estimates #this works
Then the task "task" should have 3 estimates #this one fails
EDIT
I have no custom steps - trying to use use what comes out of the box with cucumber and pickle (just to limit my confusion).
The models are
class Estimate < ActiveRecord::Base
belongs_to :Task, :class_name => "Task", :foreign_key => "Task_id"
end
and
class Task < ActiveRecord::Base
has_many :estimates
end
Can anyone point me in the right direction (or if I need to post more code)?
Thanks,
Joe
Your estimate class can look like this:
class Estimate ...
belongs_to :task
end
It will infer the table name and the fk and assuming you've been following the rails database idioms it should all just work.
As for your cuke steps, I've never used pickle, so I'm not sure what's going on with that, but if the step that's failing is:
Then the task "task" should have 3 estimates #this one fails
It might be to related to the change I've outlined above (maybe it's doing something weird with the table name??).

Resources