How to create classes in specs without polluting global namespace? - ruby-on-rails

What's the best way to define a class within the context of a spec, and not have it pollute the global namespace? How does the other file even get access to that constant?
bowling_spec.rb
require "spec_helper"
describe Bowling do
context "when validate is defined" do
let(:dummy_class) {
Class.new(described_class) do
METRICS_NAMESPACE = "ExtendedClass.Metrics.namespace"
end
}
it "does nothing" do
dummy_class
end
end
end
Spec - batting_spec.rb
require "spec_helper"
describe Batting do
context do
it "does weird thing" do
expect { self.class.const_get(:METRICS_NAMESPACE) }.to raise_error(NameError)
end
end
end
If you run the individual spec file
rspec spec/batting_spec.rb
.
Finished in 0.00285 seconds (files took 0.12198 seconds to load)
1 example, 0 failures
If you run the spec which defines the dummy class
rspec spec/bowling_spec.rb spec/batting_spec.rb
.F
Failures:
1) Batting does weird thing
Failure/Error: expect { self.class.const_get(:METRICS_NAMESPACE) }.to raise_error(NameError)
expected NameError but nothing was raised
# ./spec/batting_spec.rb:6:in `block (3 levels) in <top (required)>'
Finished in 0.01445 seconds (files took 0.12715 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/batting_spec.rb:5 # Batting does weird thing
To reproduce the error: I created a repo: https://github.com/pratik60/rspec-pollution with updated Readme

Running rspec spec/batting_spec.rb spec/bowling_spec.rb
2 examples, 0 failures
Running rspec spec/bowling_spec.rb spec/batting_spec.rb
Gives the error you mention.
Using binding.pry:
describe Batting do
context do
it "does weird thing" do
require 'pry'
binding.pry
expect { self.class.const_get(:METRICS_NAMESPACE) }.to raise_error(NameError)
end
end
end
self.class.ancestors
[RSpec::ExampleGroups::Batting::Anonymous,
RSpec::ExampleGroups::Batting::Anonymous::LetDefinitions,
RSpec::ExampleGroups::Batting::Anonymous::NamedSubjectPreventSuper,
RSpec::ExampleGroups::Batting,
RSpec::ExampleGroups::Batting::LetDefinitions,
RSpec::ExampleGroups::Batting::NamedSubjectPreventSuper,
RSpec::Core::ExampleGroup,
Going down the inheritance tree
RSpec::Core::ExampleGroup::METRICS_NAMESPACE
(pry):4: warning: toplevel constant METRICS_NAMESPACE referenced by RSpec::Core::ExampleGroup::METRICS_NAMESPACE
=> "ExtendedClass.Metrics.namespace"
Checking the first object on the chain
Object::METRICS_NAMESPACE
=>"ExtendedClass.Metrics.namespace"
Even further
Class::METRICS_NAMESPACE
(pry):2: warning: toplevel constant METRICS_NAMESPACE referenced by Class::METRICS_NAMESPACE
=> "ExtendedClass.Metrics.namespace"
TL ; DR
As you said, when you did METRICS_NAMESPACE = "ExtendedClass.Metrics.namespace", you created a global namespace constant. (That is, a constant inside the uppermost ruby constant, Class)
Simply do this instead
Class.new(described_class) do
self.const_set('METRICS_NAMESPACE', "ExtendedClass.Metrics.namespace")
end
rspec spec/bowling_spec.rb spec/batting_spec.rb
Finished in 1.55 seconds (files took 0.08012 seconds to load)
2 examples, 0 failures

Related

Run basic rspec file

I'm new to Ruby and RSpec. I want to create very simple RSpec test:
# test_spec.rb
require 'spec_helper'
describe TestController do
puts(Time.now)
end
But when I run the code this way rspec test_spec.rb I get error:
`<top (required)>': uninitialized constant TestController (NameError)
Can you give me some idea where I'm wrong?
If you pass a class to describe, RSpec attempts to create an instance of that class. If the specified class does not exist, you get an error.
You can pass a string instead:
describe "my first test" do
it "does something" do
expect(1).to be_odd
end
end
Running the above:
$ rspec -fd test_spec.rb
my first test
does something
Finished in 0.00178 seconds (files took 0.10381 seconds to load)
1 example, 0 failures
This error is because after the describe you need a string with the description and you are leaving it without quotes.
describe '3pController' do
puts(Time.now)
end
But the tests should be structured as follows
a (describe) can have many (it) inside (a (it) is where the tests are)
one (decribe) can have more (describe) inside or can have (context), that these at the same time has many (it).
inside this link I will leave the documentation Basic structure
Here you can see a basic example of a test
describe 'sum 2 + 2 equal 4' do
   it { expect(2 + 2).to eq(4)}
end

Rspec framework without cucumber and step definitions

There is need to build a rspec framework where in we don't want to write feature files and respective step definitions, to run our tests.
So far this is what I have tried this is test run, if it works I will write more.
my_example_spec.rb
require 'rspec'
describe 'Mybehaviour' do
def testMethod
visit(LoginPage).test
end
end
login_page.rb
class LoginPage
include PageObject
page_url("#{FigNewton.base_url}/login")
element :username, "input[id='username']"
element :password, "input[id='password']"
element :submit, "input[id='submit']"
def test
puts "excellent"
end
end
I am using Rubymine when I right click my example file and run it , it gives me
Run options: include {:full_description=>/Mybehaviour/}
All examples were filtered out
0 examples, 0 failures, 0 passed
Finished in 0.000315 seconds
Process finished with exit code 0
Empty test suite.
So I changed it too:
require 'rspec'
describe 'Mybehaviour' do
it 'test Method' do
visit(LoginPage).test
true.should == false
end
end
it gives me:
Run options: include {:full_description=>/Mybehaviour/}
NameError: uninitialized constant LoginPage
./my_example_spec.rb:6:in `block (2 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'
1 example, 1 failure, 0 passed
Finished in 0.001273 seconds
Process finished with exit code 1
What is the difference when I was using def and when I used it?
I understand that I need to initialize LoginPage, but where and why it is calling it as constant LoginPage instead of class LoginPage.
PS:New to rspec
My guess is you need to require login_page.rb in your spec_helper.rb (if you have one), or at the top of my_example_spec.rb
The test doesn't know about the LoginPage class, and so when it hits visit(LoginPage).test on line 6 it thinks LoginPage is an undefined constant. It doesn't know that you have defined it as a class.
Try adding:
require 'login_page'
either in your spec_helper.rb or after require 'rspec' in my_example_spec.rb

FakeFS and Rspec incosistency with Rspec and real filesystem

I'm trying to write some tests involving file operations. I want to use some fake file system (something like VCR for external services) and I have found fakeFS. Unfortunately, either I can't set it right or something is broken (which I doubt, it's quite basic function), I've prepared simple example which illustrates what I mean, let the code speak:
With real FS:
module MyModule
describe Something do
before(:all) do
File.open("#{Rails.root}/foo.txt", 'w+') { |f| f.write 'content'}
end
it 'should exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
it 'should still exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
end
end
Running that gives:
bash-4.2$ rspec
..
Finished in 0.00161 seconds
2 examples, 0 failures
Adding fakeFS in such way:
require 'fakefs/spec_helpers'
module MyModule
describe Something do
include FakeFS::SpecHelpers
FakeFS.activate!
FakeFS::FileSystem.clone(Rails.root)
before(:all) do
File.open("#{Rails.root}/foo.txt", 'w+') { |f| f.write 'content'}
end
it 'should exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
it 'should still exist' do
expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
end
end
end
results in:
bash-4.2$ rspec
.F
Failures:
1) MyModule::Something should still exist
Failure/Error: expect(Pathname.new("#{Rails.root}/foo.txt").exist?).to be_true
expected: true value
got: false
# ./spec/models/something_spec.rb:23:in `block (2 levels) in <module:MyModule>'
Finished in 0.00354 seconds
2 examples, 1 failure
So it seems like file is not persisted through subsequent tests. Do I misunderstand how before(:all) works or do I do something wrong? If so then why that code works with real files?
If it is 'not a bug, just a feature' then is there any other fake filesystem gem which is consistent with real one? Or do I have to stay with real files to get tests that.. well, test?
I found the answer just after creating that question, duh ;) I've looked into source of that lib and found suspicious line.
Instead of FakeFS::SpecHelpers I've included FakeFS::SpecHelpers::All which is the same code except FakeFS::FileSystem is not being cleared after each call, now it behaves correctly.

FactoryGirls randomly fails with 'Factory not registered', why?

I've some tests that randomly fail, approx. 20% of times. It means that WITHOUT changing the code, each time that I run the tests 1 time out of 5 will fail with "Factory not registered" error. It's very weird.. :(
This is the consone output:
Failures:
1) Unit#new_from_string returns factor for metric conversions
Failure/Error: FactoryGirl.create :taza
ArgumentError:
Factory not registered: taza
# ./spec/models/unit_spec.rb:29:in `block (2 levels) in <top (required)>'
Finished in 0.29619 seconds
4 examples, 1 failure
Failed examples:
rspec ./spec/models/unit_spec.rb:22 # Unit#new_from_string returns factor for metric conversions
Randomized with seed 61727
And this is the code:
file: "unit_spec.rb"
require 'spec_helper'
describe Unit, "#new_from_string" do
it "parses the string and returns a Unit object" do
[some tests...]
FactoryGirl.find_definitions
u = FactoryGirl.create :taza
FactoryGirl.create :tbsp
[tests...]
end
it "returns factor for metric conversions" do
[tests not involving factory girl...]
# the following is line 29, that fails
FactoryGirl.create :taza
[tests...]
end
end
file "spec/factories/units.rb":
FactoryGirl.define do
factory :taza , :class => CustomUnit do
singular 'taza'
plural 'tazas'
physical_type Unit::VOLUME
equivalence_factor 200
equivalence_unit 'ml'
end
[other factories...]
end
I think the problem is on this line
FactoryGirl.find_definitions
Actually there is no need for this line when your factories are in correct directories(I see it is), and you put gem factory_girl_rails in Gemfile.
I think, 20% of time, the second test get run at first. At this time, there is no definition of Factory and the test failed. The other test has such definition and get passed. In other time, the first test run first so definition exists.
My suggestion:
Make sure you have factory_girl_rails, not factory_girl in Gemfile.
Remove that line of definition.
[optional but recommended] Put all definition in a single file spec/factories and remove all other factory files, if you don't have too much factories. This would be easier to manage.

RSpec fails for no obvious reason

So I'm using rspec to test my code as I'm going through the Rails Tutorial, and I keep getting this error when I test the code in listing 3.20. Everything checks out when I look at it with my eyeball, but RSpec doesn't seem to like it.
(Note that I just did one of the pages, not all three because they all give the same error)
james#tristan:~/rails_projects/sample_app$
rspec
spec/controllers/pages_controller_spec.rb
F...
Failures:
1) PagesController should have the
right title
Failure/Error: response.should have_selector("title",
expected following output to contain a | Home tag:
# ./spec/controllers/pages_controller_spec.rb:13:in
`block (2 levels) in '
Finished in 0.97999 seconds 4
examples, 1 failure
james#tristan:~/rails_projects/sample_app$
At the top of that spec file it says:
before(:each) do
#
# Define #base_title here.
#
end
Does your spec assign a value to #base_title?

Resources