I've got a spec for an object that's in a number of levels of modules. Something like this:
describe Foo::Bar::Baz::Quux::Widget do
it "should == another Widget for the same Doohickey" do
doohickey = stub
Foo::Bar::Baz::Quux::Widget.new(doohickey).should == Foo::Bar::Baz::Quux::Widget.new(doohickey)
end
it "should != another Widget for a different Doohickey" do
one_doohickey = stub
another_doohickey = stub
Foo::Bar::Baz::Quux::Widget.new(one_doohickey).should == Foo::Bar::Baz::Quux::Widget.new(another_doohickey)
end
end
That's a lot of repetition, and it makes it look like I'm using an object
from some other namespace. I'd like to set the context of the spec to
Foo::Bar::Baz::Quux. The following works surprisingly well:
module Foo::Bar::Baz::Quux
describe Widget do
it "should == another Widget for the same Doohickey" do
doohickey = stub
Widget.new(doohickey).should == Widget.new(doohickey)
end
it "should != another Widget for a different Doohickey" do
one_doohickey = stub
another_doohickey = stub
Widget.new(one_doohickey).should == Widget.new(another_doohickey)
end
end
end
There's only one problem. Since I'm in Rails, I'm depending on
ActiveSupport's dependency management to autoload the Foo::Bar::Baz::Quux
module. Before, that happened when I mentioned Foo::Bar::Baz::Quux::Widget.
Now, I'm defining the module myself, so the real definition of the module in
foo/bar/baz/quux.rb is never loaded.
How can I change the constant-lookup context for my spec without defining
the module myself?
You can use the described_class helper...
describe Foo::Bar::Baz::Quux::Widget do
it "has described_class helper" do
described_class.should == Foo::Bar::Baz::Quux::Widget
end
end
Or, for the lol:
describe Foo::Bar::Baz::Quux::Widget do
def Widget
described_class
end
it "has described_class helper" do
Widget.should == Foo::Bar::Baz::Quux::Widget
end
end
Can you assign that to a variable ?
widget_class = Foo::Bar::Baz::Quux::Widget
this should DRY out the code a little bit. Just a thought.
Related
I'm trying to override the after_failed_example method so I can inflict some custom file naming on our screenshots. I'm loading the module as an initializer.
So far, so good, but the Capybara.page.current_url is blank, making me think I need to require something additional?
require "capybara-screenshot/rspec"
module Capybara
module Screenshot
module RSpec
class << self
attr_accessor :use_description_as_filename
attr_accessor :save_html_file
end
self.use_description_as_filename = true
self.save_html_file = true
def self.after_failed_example(example)
if example.example_group.include?(Capybara::DSL) # Capybara DSL method has been included for a feature we can snapshot
Capybara.using_session(Capybara::Screenshot.final_session_name) do
puts ">>>> Capybara.page.current_url: " + Capybara.page.current_url.to_s
if Capybara::Screenshot.autosave_on_failure && failed?(example) && Capybara.page.current_url != ''
saver = Capybara::Screenshot.new_saver(Capybara, Capybara.page, Capybara::Screenshot.save_html_file?, set_saver_filename_prefix(example))
saver.save
example.metadata[:screenshot] = {}
example.metadata[:screenshot][:html] = saver.html_path if saver.html_saved?
example.metadata[:screenshot][:image] = saver.screenshot_path if saver.screenshot_saved?
end
end
end
private
def self.set_saver_filename_prefix(example)
return example.description.to_s.gsub(" ", "-") if Capybara::Screenshot.use_description_as_filename?
return Capybara::Screenshot.filename_prefix_for(:rspec, example)
end
end
end
end
end
This is successfully overriding the capybara-screenshot/rspec method, and any of the Capybara::Screenshot static information is accessible, but not Capybara session related information (afa I can tell).
For example, Capybara.page.current_url.to_s is null when overridden, but present when not.
I was missing a require (kind of silly mistake):
require 'capybara/rspec'
This simple method on a class just run the status method using the safe navigation operator.
def current_status
account&.status
end
But reek report this warning:
MyClass#current_status performs a nil-check [https://github.com/troessner/reek/blob/master/docs/Nil-Check.md]
How can I properly write methods like this to avoid Nil Check?
I've also verified this post from thoughtbot but it seem like "too much" for just a safe navigation operator.
Ruby 2.3.1
The advice from "Example 4" in the linked post is verbose but pretty good :
class MyClass
def initialize(with_account = nil)
#account = Account.new if with_account
end
def current_status
account.status
end
def account
#account || NilAccount.new
end
end
class Account
def status
"Up!"
end
end
class NilAccount
def status
"Down!"
end
end
puts MyClass.new(:with_account).current_status
#=> "Up!"
puts MyClass.new.current_status
#=> "Down!"
If it's "too much" for you, account&.status might be just fine.
Whatever you do : you'll need to test your code as much as possible!
well, tell-dont-ask looks pretty good, but Example 4 looks like an overkill to resolve this specific case.
#andredurao I think, we can use this workaround to pass checks, for some reason reek is fine with it:
def current_status
return unless account
account.status
end
I am running rspec tests on a catalog object from within a Ruby app, using Rspec::Core::Runner::run:
File.open('/tmp/catalog', 'w') do |out|
YAML.dump(catalog, out)
end
...
unless RSpec::Core::Runner::run(spec_dirs, $stderr, out) == 0
raise Puppet::Error, "Unit tests failed:\n#{out.string}"
end
(The full code can be found at https://github.com/camptocamp/puppet-spec/blob/master/lib/puppet/indirector/catalog/rest_spec.rb)
In order to pass the object I want to test, I dump it as YAML to a file (currently /tmp/catalog) and load it as subject in my tests:
describe 'notrun' do
subject { YAML.load_file('/tmp/catalog') }
it { should contain_package('ppet') }
end
Is there a way I could pass the catalog object as subject to my tests without dumping it to a file?
I am not very clear as to what exactly you are trying to achieve but from my understanding I feel that using a before(:each) hook might be of use to you. You can define variables in this block that are available to all the stories in that scope.
Here is an example:
require "rspec/expectations"
class Thing
def widgets
#widgets ||= []
end
end
describe Thing do
before(:each) do
#thing = Thing.new
end
describe "initialized in before(:each)" do
it "has 0 widgets" do
# #thing is available here
#thing.should have(0).widgets
end
it "can get accept new widgets" do
#thing.widgets << Object.new
end
it "does not share state across examples" do
#thing.should have(0).widgets
end
end
end
You can find more details at:
https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#define-before(:each)-block
I'm trying to define a few let's and before hooks that will run globally for all my specs by including them in a separate file using the Rspec configuration block.
I tried something like:
module Helpers
def self.included(base)
base.let(:x){ "x" }
base.before(:all){ puts "x: #{x}" }
end
end
Rspec.configure{|c| c.include Helpers }
but this doesn't work as expected. The before(:all) doesn't just run before each main example group, but each nested one as well.
Then I found out about shared_context and it appears to be exactly what I want.
My open problem however is that I can't figure out how to share a context amongst ALL of my specs. The docs only reference include_context within a specific spec.
Can anyone tell me how I can achieve this behavior in a global manner? I'm aware that I can define global before hooks in my spec_helper but I can't seem to use let. I'd like a single place that I can define both of these things and not pollute my spec helper, but just include it instead.
I tried to reproduce your error, but failed.
# spec_helper.rb
require 'support/global_helpers'
RSpec.configure do |config|
config.include MyApp::GlobalHelpers
end
# support/global_helpers.rb
module MyApp
module GlobalHelpers
def self.included(base)
base.let(:beer) { :good }
base.before(:all) { #bottles = 10 }
end
end
end
# beer_spec.rb
require 'spec_helper'
describe "Brewery" do
it "makes good stuff" do
beer.should be :good
end
it "makes not too much bottles" do
#bottles.should == 10
end
context "when tasting beer" do
before(:all) do
#bottles -= 1
end
it "still produces good stuff" do
beer.should be :good
end
it "spends some beer on degusting" do
#bottles.should == 9
end
end
end
https://gist.github.com/2283634
When I wrote something like base.before(:all) { p 'global before'; #bottles = 10 }, I got exactly one line in spec output.
Notice that I didn't try to modify instance variables inside an example, because it wouldn't work anyway (well, actually you can modify instance variables, if it's a hash or array). Moreover, even if you change before(:all) in nested example group to before(:each), there will be still 9 bottles in each example.
I'm writing a sinatra app and testing it with rspec and rack/test (as described on sinatrarb.com).
It's been great so far, until I moved some rather procedural code from my domain objects to
sinatra helpers.
Since then, I've been trying to figure out how to test these in isolation ?
I test my sinatra helpers in isolation by putting the helper methods within its own module.
Since my sinatra application is a little bit bigger than the usual hello world example, I need to split it up into smaller parts. A module for the common helpers suits my use case well.
If you write a quick demo, and you define your helper methods within the helpers { ... } block, I don't think testing it is absolutely necessary. Any sinatra app in production, may require more modularity anyways.
# in helpers.rb
module Helpers
def safe_json(string)
string.to_s.gsub(/[&><']/) { |special| {'&' => '\u0026', '>' => '\u003E', '<' => '\u003C', "'" => '\u0027'}[special] }
end
end
# in app.rb
helpers do
include Helpers
end
# in spec/helpers_spec.rb
class TestHelper
include Helpers
end
describe 'Sinatra helpers' do
let(:helpers) { TestHelper.new }
it "should escape json to inject it as a html attribute"
helpers.safe_json("&><'").should eql('\u0026\u003E\u003C\u0027')
end
end
Actually you don't need to do:
helpers do
include FooBar
end
Since you can just call
helpers FooBar
The helpers method takes a list of modules to mix-in and an optional block which is class-eval'd in: https://github.com/sinatra/sinatra/blob/75d74a413a36ca2b29beb3723826f48b8f227ea4/lib/sinatra/base.rb#L920-L923
maybe this can help you some way http://japhr.blogspot.com/2009/03/sinatra-innards-deletgator.html
I've also tried this (which needs to be cleaned up a bit to be reusable) to isolate each helper in its own environment to be tested:
class SinatraSim
def initialize
...set up object here...
end
end
def helpers(&block)
SinatraSim.class_eval(&block)
end
require 'my/helper/definition' # defines my_helper
describe SinatraSim do
subject { SinatraSim.new(setup) }
it "should do something"
subject.expects(:erb).with(:a_template_to_render) # mocha mocking
subject.my_helper(something).should == "something else"
end
end