RSpec Matchers Not Working (Expect Not Supported) - ruby

I have researched this and every thing I've read says that the following should work:
require 'spec_helper'
require 'rspec/expectations'
include RSpec::Matchers
RSpec.describe 'Posts' do
it 'should return 200 response when getting posts' do
result_posts = RestClient.get('http://jsonplaceholder.typicode.com/posts')
expect(result_posts.code).to eq(200)
end
end
I have that in file (json_spec.rb) in my spec directory. This is using RSpec 3.5.4.
The message being received when running this spec is:
only the `receive`, `have_received` and `receive_messages` matchers
are supported with `expect(...).to`, but you have provided:
#<RSpec::Matchers::BuiltIn::Eq:0x007f9b43590f48>
One post suggested that I should be using
extend RSpec::Matchers
rather than trying to "include" them. I did that and the exact same error appears.
Yet another post suggested I should no longer be requiring "rspec/expectations" but rather just "rspec". That doesn't work either. (Yet another post said the exact opposite, of course. But at least I covered my bases there.)
Another post suggested that the include (or maybe the extend or maybe even both) had to go in an RSpec configure block, as such:
RSpec.configure do |config|
include RSpec::Matchers
end
That, however, also does not work.
What you see above is literally all that I have in my spec directory. My spec_helper.rb file initially just contained the require statements and the include directive. I moved them to the actual spec file (as shown above) just to see if that was the issue.
I'm not using Rails or Cucumber so, to my knowledge, there is no wider context in which I can, or should, be including the matchers.
I have to assume I'm missing something fairly fundamental here but none of the RSpec documentation has been much of a roadmap about this particular issue.

Thanks to #MarkoAvlijaĆĄ (see comment to post), the issue was apparently having the explicit require as well as the include statement at all.
Once those were removed, the spec file executed without issue.

I had to remove this line from spec_helper.rb: config.expect_with(:rspec) { |c| c.syntax = :should }

Related

Mocking a Browser for RSpec, Without Test Doubles Leaking

I find mocking things with RSpec to be entirely problematic and I often don't know how much code to include, in terms of it being diagnostic. So I'll start with the situation I have and the code that I've isolated as causing the problem.
I have tests where I need to mock a browser. I have a mock driver I set up like this:
require "watir"
def mock_driver
browser = double("watir")
allow(browser).to receive(:is_a?).with(Watir::Browser).and_return(true)
allow(browser).to receive(:driver).and_return(true)
browser
end
The only problems I have in my test suite are these two tests:
context "an empiric driver is requested" do
it "a watir browser is provided" do
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
Empiric.set_browser mock_driver
end
it "the requested watir browser can be shut down" do
#allow(Empiric.browser).to receive(:quit)
Empiric.quit_browser
#allow(mock_browser).to receive(:new).and_return(Empiric.browser)
#Empiric.set_browser mock_driver
end
end
(The commented out bits in the second test are on purpose to illustrate what's going on.)
With that one line in place in the second test, I get the following error on that test:
<Double "watir"> was originally created in one example but has leaked into another
example and can no longer be used. rspec-mocks' doubles are designed to only last for
one example, and you need to create a new one in each example you wish to use it for.
If I entirely comment out the first test above, that error doesn't happen so I know I've isolated the two tests that are interacting with each other.
Okay, now notice the final line of my second test that is commented out. That seems to be what the error is indicating to me. It's saying I need to create a new double in the other. Okay, so I'll change my last test:
it "the requested watir browser can be shut down" do
#allow(Empiric.browser).to receive(:quit)
Empiric.quit_browser
#allow(mock_browser).to receive(:new).and_return(Empiric.browser)
Empiric.set_browser mock_driver
end
So here I've uncommented the last line so I'm establishing the mock_driver in that test and not allowing the code to leak.
That, however, returns exactly the same error on exactly the same test.
I'm not sure if it would help to see the methods that are being called in that test, but here they are. First is set_browser:
def set_browser(app = :chrome, *args)
#browser = Watir::Browser.new(app, *args)
Empiric.browser = #browser
end
And here is quit_browser:
def quit_browser
#browser.quit
end
The fact that RSpec thought one test was "leaking" into the other made me think that perhaps my #browser instance was the problem, essentially being what's persisting between the two tests. But I don't see how to get around that. I thought that maybe if I quit the browser in the first test, that would help. So I changed the first test to this:
it "a watir browser is provided" do
Empiric.quit_browser
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
Empiric.start_browser mock_driver
end
That, however, led to the above error being shown on both tests now.
My more likely accurate guess is that I simply don't know how to provide a mock in this context.
I think you have to use allow with the mock and not Watir::Browser.
For example, what happens if you allow the mock browser to receive whatever calls the browser would and have the it return the mock browser?
Right now you're allowing the "Watir::Browser" to receive those messages and that's returning an "Empiric.browser". Looking at your code, I understand why you put that in there but I think that might be what's screwing you up here.
Mocks in RSpec are horrible things that rarely if ever work correctly in situations like this. I would entirely recommend not using the mock_driver that you have set up. Rather, for each of your tests just do something similar to what you are doing in the mock_driver. My guess is you're including the mock driver as part of a shared context and that, too, is another thing that is very fragile in RSpec. Not recommended.
Instead you might want to use contexts to break up your tests. Then for each context block have a before block. I'm not sure if you should use before:all or before:each given that you're simulating a browser. But that way you can set up the browser in the before and tear it down in an after.
But I would recommend getting it working in each test individually first. Even if it's a lot of code duplication. Then once all tests are passing, refactor to put the browser stuff in those before/after blocks.
But, again, don't use mocks. Don't use shared contexts. It never ends well and honestly it makes your tests harder to reason about.
Given some advice from Micah, I wanted to provide an answer with a solution. I ended up doing this:
context "an empiric driver is requested" do
it "a watir browser is provided" do
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
allow(Empiric.browser).to receive(:driver).and_return(true)
expect { Empiric.start_browser :some_browser }.not_to raise_error
end
it "the requested watir browser can be shut down" do
allow(Empiric.browser).to receive(:quit)
allow(Watir::Browser).to receive(:new).and_return(Empiric.browser)
allow(Empiric.browser).to receive(:driver).and_return(true)
expect { Empiric.quit_browser }.not_to raise_error
end
end
All of that was needed as it is or I would get some error or other. I removed my mock driver and, per Micah's suggestion, simply tried to incorporate what seemed to work. The above "contraption" is what I ended up with as the sweet spot.
This works in the sense of giving coverage of the methods in question. What was interesting was that I had to add this to my RSpec configuration:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.allow_message_expectations_on_nil = true
end
end
I needed to do this because RSpec was reporting that I was calling allowing something that was nil to receive a value.
This brought up some interesting things, if you think about it. I have a test that is clearly passing. And it adds to my code coverage. But is it actually testing the quit action on a browser? Well, not really since it was testing a quit action on something that it thought was nil.
But -- it does work. And it must be calling the lines of code in question because the code coverage, as reported my SimpleCov, indicates that the statements in question have been checked.

How can I mock a Ruby "require" statement in RSpec?

I have a Ruby cli program that can optionally load a user-specified file via require. I would like to unit test this functionality via RSpec. The obvious thing to do is to mock the require and verify that it happened. Something like this:
context 'with the --require option' do
let(:file) { "test_require.rb" }
let(:args) { ["--require", "#{file}"] }
it "loads the specified file"
expect(...something...).to receive(:require).with(file).and_return(true)
command.start(args)
end
end
(That's just typed, not copy/pasted - the actual code would obscure the question.)
No matter what I try, I can't capture the require, even though it's occurring (it raises a LoadError, so I can see that). I've tried a variety of things, including the most obvious:
expect(Kernel).to receive(:require).with(file).and_return(true)
or even:
let(:kernel_class) { class_double('Kernel') }
kernel_class.as_stubbed_const
allow(Kernel).to receive(:require).and_call_original
allow(Kernel).to receive(:require).with(file).and_return(true)
but nothing seems to hook onto the require
Suggestions?
So require is defined by Kernel but Kernel is included in Object so when you call require inside this context it is not necessarily the Kernel module that is processing the statement.
Update
I am not sure if this exactly solves your issue but it does not suffer from the strange behavior exhibited below:
file = 'non-existent-file'
allow(self).to receive(:require).with(file).and_return(true)
expect(self).to receive(:require).with(file)
expect(require file).to eq(true)
Working Example
OLD Answer:
This is incorrect and exists only for posterity due to the up-votes received. Some how works without the allow. Would love it if someone could explain why as I assumed it should raise instead. I believe the issue to be related to and_return where this is not part of the expectation. My guess is we are only testing that self received require, with_file, and that the and_return portion is just a message transmission (thus my updated answer)
You can still stub this like so:
file = 'non-existent-file.rb'
allow_any_instance_of(Kernel).to receive(:require).with(file).and_return(true)
expect(self).to receive(:require).with(file).and_return(true)
require file
Since I am unclear on your exact implementation since you have obfuscated it for the question I cannot solve your exact issue.

Ruby: 'require' returns false even though file not loaded

I have this code:
puts require './item'
puts $"
class Light < Item
#code
end
Item class in item.rb:
require './v3d'
require './ray'
class Item
attr_accessor :pos
def initialize(pos)
#pos = pos
end
def check(pos, dir)
return nil
end
def normal(ray)
return nil
end
end
that when I run my program prints this output:
false
enumerator.so
thread.rb
rational.so
complex.so
/usr/lib/x86_64-linux-gnu/ruby/2.3.0/enc/encdb.so
/usr/lib/x86_64-linux-gnu/ruby/2.3.0/enc/trans/transdb.so
/usr/lib/ruby/2.3.0/unicode_normalize.rb
/usr/lib/x86_64-linux-gnu/ruby/2.3.0/rbconfig.rb
/usr/lib/ruby/2.3.0/rubygems/compatibility.rb
/usr/lib/ruby/2.3.0/rubygems/defaults.rb
/usr/lib/ruby/2.3.0/rubygems/deprecate.rb
/usr/lib/ruby/2.3.0/rubygems/errors.rb
/usr/lib/ruby/2.3.0/rubygems/version.rb
/usr/lib/ruby/2.3.0/rubygems/requirement.rb
/usr/lib/ruby/2.3.0/rubygems/platform.rb
/usr/lib/ruby/2.3.0/rubygems/basic_specification.rb
/usr/lib/ruby/2.3.0/rubygems/stub_specification.rb
/usr/lib/ruby/2.3.0/rubygems/util/list.rb
/usr/lib/x86_64-linux-gnu/ruby/2.3.0/stringio.so
/usr/lib/ruby/2.3.0/rubygems/specification.rb
/usr/lib/ruby/2.3.0/rubygems/exceptions.rb
/usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_gem.rb
/usr/lib/ruby/2.3.0/monitor.rb
/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb
/usr/lib/ruby/2.3.0/rubygems.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/version.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/core_ext/name_error.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/levenshtein.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/jaro_winkler.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkable.rb
/usr/lib/ruby/2.3.0/delegate.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkers/name_error_checkers.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkers/method_name_checker.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/spell_checkers/null_checker.rb
/usr/lib/ruby/vendor_ruby/did_you_mean/formatter.rb
/usr/lib/ruby/vendor_ruby/did_you_mean.rb
/home/<user>/Documents/ruby/ray/write_ppm.rb
/home/<user>/Documents/ruby/ray/v3d.rb
/home/<user>/Documents/ruby/ray/pixel.rb
/home/<user>/Documents/ruby/ray/image.rb
/home/<user>/Documents/ruby/ray/material.rb
then throws:
/home/<user>/Documents/ruby/ray/light.rb:4:in `<top (required)>': uninitialized constant Item (NameError)
When require './item' is called, there is no error AND it returns false. From my understanding of how require works, it seems that the program incorrectly thinks it does not need to load item.rb. Why does this happen and how can I fix it?
Edit: expanded on some code
As a generic answer, not related to op, but because I had a similar issue and was pointed here by search engines.
Basically require 'http' returned false while gem was not loaded.
I figured out that there is a http.rb file inside load path and it is being loaded instead of the standard gem. So double check there are no conflicting file names of ruby files under library load path and the gem name.
I solved the problem by totally rewriting my require statements for every file. What I think the problem was, was this:
item.rb contained require './ray'
ray.rb contained require './light'
light.rb contained require './item' and class Light < Item
While loading item.rb, the interpreter saw it needed to also load ray.rb and therefore light.rb. When it reached the require './item' inside light.rb, it returned false because it was in the process of loading that file. However, since it was not yet finished loading, it did not show up in $". The interpreter then needed access to the definition of the Item class to finish loading light.rb, but because it needed to finish loading light.rb to load item.rb, the interpreter thew a NameError.
I think you may want require_relative instead.
http://ruby-doc.org/core-2.4.0/Kernel.html#method-i-require_relative
vs
http://ruby-doc.org/core-2.4.0/Kernel.html#method-i-require
You are correct in saying that if require 'my_lib' returns false, then 'my_lib' has already been loaded. However this is different from saying that a MyLib class is defined. Does your item.rb define an Item class?
Also, it may be possible that Item is defined somewhere else in the namespace hierarchy. e.g. if your item.rb is in some_gem/item.rb, and you're calling require from some_gem/, it will load successfully, but the name of the class might be SomeGem::Item. In this case you wouldn't be able to access it directly from the root namespace.
Last thing I can think of is that the item.rb file is changing under, or otherwise has some very dynamic pieces that are confusing the interpreter.
I would think that the issue is one of these before thinking that require is somehow messing up.

How to create a sandboxed RSpec environment?

Essentially, I want to create a program that will run some untrusted code that defines some method or class, and then run an untrusted rspec spec against it.
I've looked into sandboxing Ruby a bit, and this video from rubyconf was particularly helpful. After looking at several solutions, the two that appear to be the most helpful are rubycop, which essentially does static analysis on the code, and the jruby sandbox (both covered in above video). My instinct tells me that the jruby sandbox is probably safer, but I could well be wrong.
Here's a completely unsafe example of what I want to do:
code = <<-RUBY
class Person
def hey
"hey!"
end
end
RUBY
spec = <<-RUBY
describe Person do
let(:person) { Person.new }
it "says hey" do
person.hey.should == "hey!"
end
end
RUBY
# code and spec will be from user input (unsafe)
eval code
require 'rspec/autorun'
eval spec
Which all works fine, but the code obviously needs to be sandboxed. It will be a matter of minutes before some genius submits system("rm -rf /*"), fork while fork or something equally dangerous.
I made various attempts with the jruby sandbox...
sand = Sandbox::Safe.new
sand.eval("require 'rspec/autorun'")
sand.activate! # lock it down
sand.eval code
puts sand.eval spec
That code throws this exception:
Sandbox::SandboxException: NoMethodError: undefined method `require' for #<RSpec::Core::Configuration:0x7c3cfaab>
This is because RSpec tries to require some stuff after the sandbox has been locked down.
So, I tried to force RSpec to require stuff before the sandbox gets locked down by calling an empty describe:
sand = Sandbox::Safe.new
sand.eval("require 'rspec/autorun'")
sand.eval("describe("") { }")
sand.activate! # lock it down
sand.eval code
sand.eval spec
And I get this:
Sandbox::SandboxException: NameError: uninitialized constant RSpec
Which basically means that RSpec doesn't exist in the sandbox. Which is odd, considering sand.eval("require 'rspec/autorun'") returns true, and that the earlier example actually worked (RSpec's autoloader started to run).
It may be a problem with gems and this particular sandbox though. The sandbox object actually supports a method #require, which is essentially bound to Kernel.require, and therefore can't load gems.
It's starting to look like using this sandbox just might not really be possible with rspec. The main problem is trying to actually load it into the sandbox. I even tried something like this:
require 'rspec'
sand.ref(RSpec) # open access to local rspec
But it wasn't having any of it.
So, my question is two-fold:
Does anyone have any bright ideas on how to get this to work with the jruby sandbox?
If not, how secure is rubycop? Apparently codeschool use it, so it must be pretty well tested... it would be nice to be able to use ruby 1.9 instead of jruby as well.
It looks like the sand box environment isn't loading the bundle/gemset. RVM could be at fault here if you are using a gemset or something.
One might try loading the Bundle again once sand boxed.
I would look at ruby taint modes
$SAFE The security level
0 --> No checks are performed on externally supplied (tainted) data. (default)
1 --> Potentially dangerous operations using tainted data are forbidden.
2 --> Potentially dangerous operations on processes and files are forbidden.
3 --> All newly created objects are considered tainted.
4 --> Modification of global data is forbidden.
I have been trying to figure out a similar problem. I want to use some gems like json and rest-client inside my sandbox after activating it. I tried following.
require "sandbox"
s=Sandbox.safe
s.eval <<-RUBY
require 'bundler'
Bundler.require :sandbox
RUBY
s.activate!
Gemfile.rb
group :sandbox do
platforms :jruby do
gem 'json'
gem 'rest-client'
end
end
This way, I was able to require gems in my sandbox. But, then there were some gem specific issues with sandbox. For eg, I had to add a method initialize_dup to whitelist for safe.rb in jruby-sandbox. RestClient has some problem with Fake File Sytem ALT_SEPARATOR which I am trying to patch. You can try this approach for RSpec and see if everything goes through.

Requiring gem in Rails 3 Controller failing with "Constant Missing"

I've seen this asked a few times in other threads, but none of the answers seem to apply.
Environment:
Rails 3
amazon/ecs gem from jugend. The lone file is here:
http://github.com/jugend/amazon-ecs/blob/master/lib/amazon/ecs.rb
my gemfile has:
gem 'amazon-ecs', :git => 'git://github.com/jugend/amazon-ecs.git'
Everything works in irb. I can run:
bundle console
require 'amazon/ecs' and then go to town
when I try to use it from the controller though, like so:
require 'amazon/ecs'
require 'amazon/ecs'
class SearchController < ApplicationController
def index
end
def results
Amazon::Ecs.configure do |options|
options[:aWS_access_key_id] = '[key]'
options[:aWS_secret_key] = '[secret]'
end
res = Amazon::Ecs.item_search(params[:search], {:response_group => 'Medium', :search_index => 'All'})
end
end
I get: uninitialized constant SearchController::Amazon at line 8, where I first try to use Amazon.
the ecs.rb has a module Amazon containing a class Ecs. I'm not sure why this is working in erb, and not in rails.
I'm still kinda new to Rails, so please answer using small words. :-/
Was given the answer. I moved my initialization code to an initializer in config/initializers file, removed the require entirely, and things worked. I don't know why though, so if someone could answer that, that'd be great.
All of the gems require their files by default, so usually you don't need to explicitly require any files.
Speaking about your problem, it could somehow be, that your controller is run before Amazon module is processed.

Resources