Added Timeout to method, and now the first spec always fails - ruby

I recently added a timeout to a method that is used by about a dozen different commands in the class. Here is the method:
def exec_mc!(command)
begin
Timeout.timeout(30.minutes) do
library_motion_logger ||= ::Logging.logger["#library_logger.name}::Motion"]
library_motion_logger.info("Executing mc command 'mc #{command}' ...")
result = rcm_ssh.execute("/RCM_code/mc #{command}")
# TODO: Can this be the result.success method instead, which checks for empty stderr also
# Match the time pattern from mc
success = result.exit_code.zero?
MotionCommandResult.new(success, result.execution_time, result.stdout, result.stderr)
end
rescue Timeout::Error
# Something is wrong. Fail out now.
library_motion_logger.warn("Timed out while waiting for mc command '#{command}'")
false
end
end
Now when I run rspec, the first spec fails no matter which one is ran first. If I run a single spec, that also fails. Here is what I am seeing as the failure:
1) Library::Motion::MotionCommander#gather behaves like basic motion command with successful result should receive execute("/RCM_code/mc --gather", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
As you can see, it says that the error occurs while trying to get the logger in the very first line of my begin block. If I remove this line, I get the same error on the next line. So basically I am always getting an error on the first line of the first spec, no matter what. I have tried moving things around and this has held true. What can I do to fix this?
Edit: Here are some of the specs
def stub_and_expect_mc(exits = [], stderrs = [], stdouts = [])
full_command = "/RCM_code/mc #{command}"
ssh_stub_and_expect(full_command, exits, stderrs, stdouts)
end
shared_examples 'basic motion command' do
context 'with successful result' do
before :each do
stub_and_expect_mc(0)
end
it { is_expected.to have_attributes(success: true) }
end
context 'with unsuccessful result' do
before :each do
stub_and_expect_mc(1)
end
it { is_expected.to have_attributes(success: false) }
end
end
describe '#random_slot_to_slot' do
let(:command) { '--randomslot' }
subject { #library.random_slot_to_slot }
it_behaves_like 'basic motion command'
end
describe '#drawer_to_drawer' do
# Extra space before --srcdrawer since no mover number is specified
let(:command) { "--drawertodrawer --srcdrawer #{src_drawer} --destdrawer #{dest_drawer} --force" }
let(:src_drawer) { SpectraLibrary::Motion::HydraDrawer.new(0, 0, 1, 0, '', '', 1) }
let(:dest_drawer) { SpectraLibrary::Motion::HydraDrawer.new(0, 0, 2, 0, '', '', 2) }
subject { #library.drawer_to_drawer(src_drawer, dest_drawer) }
it_behaves_like 'basic motion command'
end
Edit 2: Here are the last three times I ran specs, with the rescue removed so you can see the actual failure (otherwise it just shows the result was false)
Failures:
1) SpectraLibrary::Motion::MotionCommander#gather behaves like basic motion command with successful result should receive execute("/RCM_code/mc --gather", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:80
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:148:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:95:in `gather'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:79:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.09762 seconds (files took 0.72568 seconds to load)
30 examples, 1 failure
Failures:
1) SpectraLibrary::Motion::MotionCommander#drawer_to_drawer behaves like basic motion command with successful result should receive execute("/RCM_code/mc --drawertodrawer --srcdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055710f024e20> --destdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055710f024d30> --force", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:42
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:149:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:29:in `drawer_to_drawer'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:41:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.08525 seconds (files took 0.75393 seconds to load)
30 examples, 1 failure
Failures:
1) SpectraLibrary::Motion::MotionCommander#drawer_to_drawer behaves like basic motion command with successful result should receive execute("/RCM_code/mc --drawertodrawer --srcdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055ef29d2efd8> --destdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055ef29d2ef38> --force", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:42
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:149:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:29:in `drawer_to_drawer'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:41:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.10332 seconds (files took 0.73916 seconds to load)
30 examples, 1 failure

Related

Rspec: No such file or directory # rb_sysopen -

I'm trying to write a simple test for a ruby script that uses gets and I'm experiencing some weird behavior.
When I run the test with rspec it passes fine, but as soon as I run rspec with any flags, like rspec -f json or rspec -O rand the test will fail and give me No such file or directory # rb_sysopen - -O, where -O is whatever the flag that was run.
Stranger still is that if I run rspec and specify the test file like rspec spec/gets_spec.rb, I get a completely different error:
Failure/Error: expect { require_relative '../gets' }.to output("Hello, Lena!\n").to_stdout
expected block to output "Hello, Lena!\n" to stdout, but output "Hello, Describe \"gets.rb\" do!\n"
Diff:
## -1,2 +1,2 ##
-Hello, Lena!
+Hello, Describe "gets.rb" do!
# ./spec/gets_spec.rb:14:in `block (2 levels) in <top (required)>'
I'm having trouble figuring out the right way to write my test so that this doesn't happen but I'm not sure what part of my code needs to change.
I'm using ruby 2.6.3
I learned about testing input from this question
The script, gets.rb, looks like
puts "Hello, #{gets.chomp.capitalize}!"
My test looks like
describe "gets.rb" do
before do
$stdin = StringIO.new("lena")
end
after do
$stdin = STDIN
end
it "should output 'Hello, name!'" , points: 1 do
allow($stdin).to receive(:gets).and_return("lena")
expect { require_relative "../gets" }.to output("Hello, Lena!\n").to_stdout
end
end
Here's the full failure message:
Failures:
1) gets.rb should output 'Hello, name!'
Failure/Error: expect { require_relative '../gets' }.to output("Hello, Lena!\n").to_stdout
Errno::ENOENT:
No such file or directory # rb_sysopen - -O
#./gets.rb:1:in `gets'
# ./gets.rb:1:in `gets'
# ./gets.rb:1:in `<top (required)>'
# ./spec/gets_spec.rb:14:in `require_relative'
# ./spec/gets_spec.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/gets_spec.rb:14:in `block (2 levels) in <top (required)>'
You can try to use a little different approach:
describe "gets.rb" do
it "should output 'Hello, name!'" do
allow_any_instance_of(Object).to receive(:gets).and_return("lena")
expect { require_relative "../gets.rb" }.to output("Hello, Lena!\n").to_stdout
end
end
allow_any_instance_of I took from https://makandracards.com/makandra/41096-stubbing-terminal-user-input-in-rspec
Reading user input in console applications is usually done using Kernel#gets.
The page suggests to use Object.any_instance.stub(gets: 'user input') but it's deprecated syntax already and I've updated it.

Mongoid and Rspec error Mongo::Error::NoServerAvailable:

When I am starting my Rspec test, I get this error:
1) User checks if the user is created
Failure/Error: expect{ #user_test = create(:user) }.to change { User.count }
Mongo::Error::NoServerAvailable:
No server is available matching preference: #<Mongo::ServerSelector::Primary:0x70316515164800 tag_sets=[] max_staleness=nil> using server_selection_timeout=30 and local_threshold=0.015
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongo-2.5.0/lib/mongo/server_selector/selectable.rb:115:in `select_server'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongo-2.5.0/lib/mongo/collection/view/readable.rb:139:in `block in count'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongo-2.5.0/lib/mongo/retryable.rb:44:in `read_with_retry'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongo-2.5.0/lib/mongo/collection/view/readable.rb:138:in `count'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongoid-5.2.1/lib/mongoid/contextual/mongo.rb:70:in `block in count'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongoid-5.2.1/lib/mongoid/contextual/mongo.rb:504:in `try_cache'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongoid-5.2.1/lib/mongoid/contextual/mongo.rb:70:in `count'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongoid-5.2.1/lib/mongoid/contextual.rb:20:in `count'
# /Users/aliceguillaume/.rvm/gems/ruby-2.3.4/gems/mongoid-5.2.1/lib/mongoid/findable.rb:55:in `count'
# ./spec/models/user_spec.rb:11:in `block (3 levels) in <top (required)>'
# ./spec/models/user_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 30.14 seconds (files took 8.39 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:10 # User checks if the user is created
I am really confused about it.
My mongo server is running too.
My user_spec.rb:
require 'spec_helper'
require 'rails_helper'
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
RSpec.describe User, :type => :model do
it "checks if the user is created" do
expect{ #user_test = create(:user) }.to change { User.count }
end
end
Any idea why my mongo won't work?
Found the answer
My config file had mistake so I change it to :
test:
clients:
default:
database: test
hosts:
- 127.0.0.1:27017
options:
read:
mode: :primary
max_pool_size: 1
log_level: :debug

If I'm testing an rspec extension, how do I suppress the results of tests which fail as part of the test?

I'm trying to write specs for an extension to rspec.
This is the gist of what I'm trying to test:
require 'rspec-let-and-after-extension'
RSpec.describe "let(...).and_after" do
it 'is called if the `let` is invoked even if the example fails' do
call_order = []
RSpec.describe do
let(:foo) { }.and_after { call_order << :and_after }
it { foo; call_order << :example; raise 'failed!' }
end.run
expect(call_order).to eq [:example, :and_after]
end
end
One of the important behaviours is that if running the example fails, the cleanup code still runs. So I test this by recording the order of the calls and raising an exception from the example.
Problem is, when I run it, it sees this block as a second example, which then fails with errors:
.F
Failures:
1)
Got 0 failures and 2 other errors:
1.1) Failure/Error: it { foo; call_order << :example; raise 'failed!' }
RuntimeError:
failed!
# ./spec/spec.rb:43:in `block (4 levels) in <top (required)>'
# ./spec/spec.rb:44:in `block (2 levels) in <top (required)>'
1.2) Failure/Error: it { foo; call_order << :example; raise 'failed!' }
RuntimeError:
failed!
# ./spec/spec.rb:43:in `block (4 levels) in <top (required)>'
Finished in 0.00167 seconds (files took 0.08011 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/spec.rb:43 #
As you can see, the output did have one dot, so the actual example passed. But then there is an F, because it has seen the internal example, run that, and unsurprisingly that one failed.
How do I make rspec not see this nested example as one of the examples it's supposed to run, so that this example completes with a single dot?
(If you're wondering about what the rspec devs themselves do about their tests, it looks like they use cucumber. Do they use cucumber because they couldn't figure this out either? :))
You can use the new sandboxing API (available in 3.2+).
RSpec.configure do |rspec|
rspec.around do |ex|
RSpec::Core::Sandbox.sandboxed do |config|
# re-configure any configuration defined by your extension here
# before allowing the example to run. The sandbox runs with a fresh
# config instance, which means any configuration you have set in
# `rspec-let-and-after-extension` will not apply while the example
# is running.
# config.extend MyExtensionModule
ex.run
end
end
end

Ruby script raising unexpected backtrace

I have a method that should raise a custom error with a message. When I catch the error and raise my own custom error, it is still raising and printing the backtrace of the original error. I just want the custom error and message. Code below.
Method:
def load(configs)
begin
opts = {access_token: configs['token'],
api_endpoint: configs['endpoint'],
web_endpoint: configs['site'],
auto_paginate: configs['pagination']}
client = Octokit::Client.new(opts)
repos = client.org_repos(configs['org'])
repos.each do |r|
Project.create(name: r.name)
end
rescue Octokit::Unauthorized
raise GitConfigError, "boom"
end
#rescue Octokit::Unauthorized
end
class GitConfigError < StandardError
end
My test (which is failling):
context 'with incorrect git configs' do
before do
allow(loader).to receive(:load).and_raise Octokit::Unauthorized
end
it { expect{loader.load(configs)}.to raise_error(GitConfigError, "boom" ) }
end
Test Output:
GitProjectLoader#load with incorrect git configs should raise GitConfigError with "boom"
Failure/Error: it { expect{loader.load(configs)}.to raise_error(GitConfigError, "boom" ) }
expected GitConfigError with "boom", got #<Octokit::Unauthorized: Octokit::Unauthorized> with backtrace:
# ./spec/lib/git_project_loader_spec.rb:24:in `block (5 levels) in <top (required)>'
# ./spec/lib/git_project_loader_spec.rb:24:in `block (4 levels) in <top (required)>'
# ./spec/lib/git_project_loader_spec.rb:24:in `block (4 levels) in <top (required)>'
If you intend to test the handling of the Octokit::Unauthorized error, then raise the error anywhere before the rescue kicks in. Preferably, someplace where it would actually be raised.
Something like this, for example:
before do
allow(Octokit::Client).to receive(:new).and_raise(Octokit::Unauthorized)
end
And then:
expect{ loader.load(configs) }.to raise_error(GitConfigError, "boom" )
As a side note, I would discourage enclosing all lines of your method in a begin;rescue;end structure; you should enclose only the lines from which you are expecting errors.
You are not testing your code as you think. You have mocked it out.
The line
allow(loader).to receive(:load).and_raise Octokit::Unauthorized
replaces the load method on loader with a stub which just raises the named error.
Remove your before block, and it should test your code as intended. Note as written it will make a real request via Octokit, unless you mock that out instead.

Pure Ruby rspec test passes without method being defined

I have an rspec test on a pure Ruby model:
require 'spec_helper'
require 'organization'
describe Organization do
context '#is_root?' do
it "creates a root organization" do
org = Organization.new
expect { org.is_root?.to eq true }
end
end
end
My organization model looks like this:
class Organization
attr_accessor :parent
def initialize(parent = nil)
self.parent = parent
end
end
The output when running the tests:
bundle exec rspec spec/organization_spec.rb:6
Run options: include {:locations=>{"./spec/organization_spec.rb"=>[6]}}
.
Finished in 0.00051 seconds
1 example, 0 failures
When I run the test, it passes, despite the fact that the method is_root? doesn't exist on the model. I usually work in Rails, not pure Ruby, and I've never seen this happen. What is going on?
Thanks!
It should be:
expect(org.is_root?).to eq true
When you pass block to expect it is being wrapped in ExpectationTarget class (strictly speaking BlockExpectationTarget < ExpectationTarget). Since you didn't specify what you expect from this object, the block is never executed, hence no error is raised.
You are passing a block to expect, which is never being called. You can see this by setting an expectation on that block
expect { org.is_root?.to eq true }.to_not raise_error
1) Organization#is_root? creates a root organization
Failure/Error: expect { puts "HI";org.is_root?.to eq true }.to_not raise_error
expected no Exception, got #<NoMethodError: undefined method `is_root?' for #<Organization:0x007ffa798c2ed8 #parent=nil>> with backtrace:
# ./test_spec.rb:15:in `block (4 levels) in <top (required)>'
# ./test_spec.rb:15:in `block (3 levels) in <top (required)>'
# ./test_spec.rb:15:in `block (3 levels) in <top (required)>'
Or by just putting a plain raise or puts inside the block, neither of which will be called:
expect { puts "HI"; raise; org.is_root?.to eq true }
The block form is used for expecting that a piece of code raises an exception or not. The correct syntax for checking values is:
expect(org.is_root?).to eq(true)

Resources