Private method 'puts'? Can't pass cucumber test in RSpec Book's Codebreaker example - ruby

I'm following along in the RSpec Book and I can't get the cucumber test to pass a certain spot. I've even tried running cucumber on the source code from the book and it still won't pass. I'm not sure if it's the fact that I'm using a newer version of cucumber but there must be a way to make it pass!
When I get to the point where the program should start a new game I'm told there's an undefined method 'puts'. Why is it undefined and how else should I capture what the program puts out in Cucumber? Running the test in RSpec to make sure the game puts the message works just fine, not sure how to make it pass in cucumber.
(Cucumber version 1.3.17, Ruby version 2.1.2)
Basically, I have these features:
Feature: code-breaker starts game
As a code-breaker
I want to start a game
So that I can break the code
Scenario: start game
Given I am not yet playing
When I start a new game
Then I should see "Welcome to Codebreaker!"
And I should see "Enter guess:"
and these step definitions:
class Output
def messages
#messages ||= []
end
def puts(message)
messages << message
end
end
def output
#output ||= Output.new
end
Given /^I am not yet playing$/ do
end
When /^I start a new game$/ do
game = Codebreaker::Game.new(output)
game.start
end
Then /^I should see "([^"]*)"$/ do |message|
output.messages.should include(message)
end
and this ruby code:
module Codebreaker
class Game
def initialize(output)
#output = output
end
def start
#output.puts 'Welcome to Codebreaker!'
end
end
end
and I keep getting this error:
When I start a new game # features/step_definitions/codebreaker_steps.rb:16
private method `puts' called for #<RSpec::Matchers::BuiltIn::Output:0x007fa4ea200ad0> (NoMethodError)
./lib/codebreaker/game.rb:10:in `start'
./features/step_definitions/codebreaker_steps.rb:18:in `/^I start a new game$/'
features/codebreaker_starts_game.feature:7:in `When I start a new game'
How do I fix this so that it passes in cucumber?

There are a lot of things that have changed since this RSpec Book was published but I got it working by replacing your code_break_steps.rb file with this:
Given /^I am not yet playing$/ do
end
When /^I start a new game$/ do
#messenger = StringIO.new
game = Codebreaker::Game.new(#messenger)
game.start
end
Then /^I should see "([^"]*)"$/ do |message|
expect(#messenger.string.split("\n")).to include(message)
end
RSpec 3 has a class that you're trying to overwrite called Output and it was causing way too much confusion. I just simplified this so you're tests are still talking to each other along with stdout.
You can use the same should syntax on the last line but it will be deprecated soon so better to get used to the expect syntax I used. For now if that's more confusing you can still use:
#messenger.string.split("\n").should include(message)

I'm following this book right now and tried Anthony's technique. But I got into trouble again a little later in the book (most probably because I'm kind of a beginner in Rails and couldn't translate Anthony's technique to another case ^^). But anyway, I found an easier way not to interfere with RSpec Output class and still not divert too much form the lesson flow:
rename the project Output class to something else, say OOutput
and change all output variables to o_output.
Et voilĂ  ;-)

I was able to make that particular example pass in only two adjustments on the same "codebreaker_steps.rb" file, so you can still follow the book's curriculum. You don't have to touch the class name, just the output() method following it.
RSpec 3 contains an output() method that causes conflicts when you try to define another output() method. So in knowing that, take the book's instructions:
def output
#output ||= Output.new
end
and rename it to:
def see_output #new
#output ||= Output.new
end
(or name it anything that makes sense for the project)
Then go down to your When() method definition:
When /^I start a new game$/ do
game = Codebreaker::Game.new(output)
game.start
end
and update the second line to:
When ("I start a new game") do
game = Codebreaker::Game.new(see_output) #new
game.start
end

Related

Rspec run before block once before multiple it blocks

I have a context block in rspec with multiple it blocks inside it. I want to run a before block to set up data for the it blocks but this data takes a long time to set up and is being used to read only. rspec before(:each) creates and deletes this data after every it block which takes a long time. before(:all) creates the data at the start of all the tests and doesn't delete it.
Is there any way I can have this data created just within the context block and deleted after?
Since RSpec 3 these are officially named before/after(:{example,context}) (see docs).
What you want to accomplish can be done with a before(:context) where you set up the data and an after(:context) where you clean it up.
RSpec.describe Thing do
before(:context) do
#thing = Thing.new
end
after(:context) do
#thing.delete
end
it "has 0 widgets" do
expect(#thing.widgets.count).to eq(0)
end
it "can accept new widgets" do
#thing.widgets << Object.new
end
it "shares state across examples" do
expect(#thing.widgets.count).to eq(1)
end
end

Upgrading Rspec to Rspec 2.99, want to use let connection variable in before/after hooks

I have a head scratcher. When upgrading rspec I am getting:
DEPRECATION: let declaration `directory` accessed in an `after(:all)` hook
at:
`let` and `subject` declarations are not intended to be called
Now I understand that I cannot use let defined variables in before/after hooks. However, the methods that are used with my test suite uses a connection to preform some REST API action:
let {:connection} {user_base}
after(:all) do
connection.delete_folder
end
My question is this: Is there anyway of getting around this without making every connection an instance variable? I want to avoid calling connection variable each time I want to preform an action e.g.
before(:all) do
#connection = user_base
end
it "adds folder" do
#connection.add_folder
end
it "edits folder" do
#connection.edit_folder
end
I think RSpec wants you to run the block before each example instead of your once before all examples:
let(:connection) { user_base }
after do # short for `after(:each) do`
connection.delete_folder
end
it "adds folder" do
connection.add_folder
end
it "edits folder" do
connection.edit_folder
end

Disable puts output in RSpec

I got a ProgressBar with some logs, and it is being displayed on each iteration. When I run RSpec, I'd like that only the results to get displayed (only . or f, and not the output that my app may display).
Between, I'm not using rails, so I don't have environments.
I got this:
And I want this:
My code is deadly simple:
progress_bar = ProgressBar.create(:total => nil)
progress_bar.increment
progress_bar.log "New Page added on depth #{depth}! #{url}"
any_instance is deprecated since RSpec 3.
Also the ProgressBar inner classes seem to have changed. This did it for me :
before(:each) do
allow_any_instance_of(ProgressBar::Output).to receive(:stream).and_return(double().as_null_object)
end
Just stub logging in tests:
ProgressBar.any_instance.stub(:log)
if there are more than 1 test for this action, put it in before(:each):
before(:each) do
ProgressBar.any_instance.stub(:log)
end
If you use progressbar gem, it will create ProgressBar::Base instance, so you should use:
ProgressBar::Base.any_instance.stub(:log)

Data driven testing with ruby testunit

I have a very basic problem for which I am not able to find any solution.
So I am using Watir Webdriver with testunit to test my web application.
I have a test method which I would want to run against multiple set of test-data.
While I can surely use old loop tricks to run it but that would show as only 1 test ran which is not what I want.
I know in testng we have #dataprovider, I am looking for something similar in testunit.
Any help!!
Here is what I have so far:
[1,2].each do |p|
define_method :"test_that_#{p}_is_logged_in" do
# code to log in
end
end
This works fine. But my problem is how and where would I create data against which I can loop in. I am reading my data from excel say I have a list of hash which I get from excel something like
[{:name =>abc,:password => test},{:name =>def,:password => test}]
Current Code status:
class RubyTest < Test::Unit::TestCase
def setup
#excel_array = util.get_excel_map //This gives me an array of hash from excel
end
#excel_array.each do |p|
define_method :"test_that_#{p}_is_logged_in" do
//Code to check login
end
end
I am struggling to run the loop. I get an error saying "undefined method `each' for nil:NilClass (NoMethodError)" on class declaration line
You are wanting to do something like this:
require 'minitest/autorun'
describe 'Login' do
5.times do |number|
it "must allow user#{number} to login" do
assert true # replace this assert with your real code and validation
end
end
end
Of course, I am mixing spec and test/unit assert wording here, but in any case, where the assert is, you would place the assertion/expectation.
As this code stands, it will pass 5 times, and if you were to report in story form, it would be change by the user number for the appropriate test.
Where to get the data from, that is the part of the code that is missing, where you have yet to try and get errors.

How can I run Selenium (used through Capybara) at a lower speed?

By default Selenium runs as fast as possible through the scenarios I defined using Cucumber.
I would like to set it to run at a lower speed, so I am able to capture a video of the process.
I figured out that an instance of Selenium::Client::Driver has a set_speed method. Which corresponds with the Java API.
How can I obtain an instance of the Selenium::Client::Driver class? I can get as far as page.driver, but that returns an instance of Capybara::Driver::Selenium.
Thanks to http://groups.google.com/group/ruby-capybara/msg/6079b122979ffad2 for a hint.
Just a note that this uses Ruby's sleep, so it's somewhat imprecise - but should do the job for you. Also, execute is called for everything so that's why it's sub-second waiting. The intermediate steps - wait until ready, check field, focus, enter text - each pause.
Create a "throttle.rb" in your features/support directory (if using Cucumber) and fill it with:
require 'selenium-webdriver'
module ::Selenium::WebDriver::Firefox
class Bridge
attr_accessor :speed
def execute(*args)
result = raw_execute(*args)['value']
case speed
when :slow
sleep 0.3
when :medium
sleep 0.1
end
result
end
end
end
def set_speed(speed)
begin
page.driver.browser.send(:bridge).speed=speed
rescue
end
end
Then, in a step definition, call:
set_speed(:slow)
or:
set_speed(:medium)
To reset, call:
set_speed(:fast)
This will work, and is less brittle (for some small value of "less")
require 'selenium-webdriver'
module ::Selenium::WebDriver::Remote
class Bridge
alias_method :old_execute, :execute
def execute(*args)
sleep(0.1)
old_execute(*args)
end
end
end
As an update, the execute method in that class is no longer available. It is now here only:
module ::Selenium::WebDriver::Remote
I needed to throttle some tests in IE and this worked.
The methods mentioned in this thread no longer work with Selenium Webdriver v3.
You'll instead need to add a sleep to the execution command.
module Selenium::WebDriver::Remote
class Bridge
def execute(command, opts = {}, command_hash = nil)
verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
path = path.dup
path[':session_id'] = session_id if path.include?(':session_id')
begin
opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
rescue IndexError
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
end
Selenium::WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
res = http.call(verb, path, command_hash)
sleep(0.1) # <--- Add your sleep here.
res
end
end
end
Note this is a very brittle way to slow down the tests since you're monkey patching a private API.
I wanted to slow down the page load speeds in my Capybara test suite to see if I could trigger some intermittently failing tests. I achieved this by creating an nginx reverse proxy container and sitting it between my test container and the phantomjs container I was using as a headless browser. The speed was limited by using the limit_rate directive. It didn't help me to achieve my goal in the end, but it did work and it may be a useful strategy for others to use!

Resources