How to reuse scenarios within different features with rspec + capybara - ruby-on-rails-3

Say I have some scenarios that I want to test under different contexts, or "features".
For example, I have scenarios which involve the user visiting certain pages and expecting certain ajax results.
But, under different conditions, or "features", I need to perform different "background" tasks which change the state of the app.
In this case I need to run the same scenarios over and over again to make sure that everything works with the different changes to the state of the app.
Is there a way to define scenarios somewhere, and then reuse them?

You can create re-usable scenarios that are used in multiple features by using shared examples.
A basic example, taken from the relishapp page is below. As you can see, the same scenarios are used in multiple features to test different classes - ie there are 6 examples run.
require 'rspec/autorun'
require "set"
shared_examples "a collection" do
let(:collection) { described_class.new([7, 2, 4]) }
context "initialized with 3 items" do
it "says it has three items" do
collection.size.should eq(3)
end
end
describe "#include?" do
context "with an an item that is in the collection" do
it "returns true" do
collection.include?(7).should be_true
end
end
context "with an an item that is not in the collection" do
it "returns false" do
collection.include?(9).should be_false
end
end
end
end
describe Array do
it_behaves_like "a collection"
end
describe Set do
it_behaves_like "a collection"
end
There are several examples on the relishapp page, including running the shared examples with parameters (copied below). I would guess (since I do not know your exact tests) that you should be able to use the parameters to setup the different conditions prior to executing the set of examples.
require 'rspec/autorun'
shared_examples "a measurable object" do |measurement, measurement_methods|
measurement_methods.each do |measurement_method|
it "should return #{measurement} from ##{measurement_method}" do
subject.send(measurement_method).should == measurement
end
end
end
describe Array, "with 3 items" do
subject { [1, 2, 3] }
it_should_behave_like "a measurable object", 3, [:size, :length]
end
describe String, "of 6 characters" do
subject { "FooBar" }
it_should_behave_like "a measurable object", 6, [:size, :length]
end

Related

Whats the right way to test an array of model values in rspec?

I've yet to get my head around the rspec way of writing tests, but would appreciate some help in understanding what I've done wrong with this test.
describe Source do
describe "Upcase name" do
names = {'bob' => 'Bob',
'edward jones' => 'Edward Jones'
'Edward jones' => 'Edward Jones'}
names.each do |name,expect|
before { #source = Source.create(name: name) }
after { #source.destroy! }
it "source #{name} should be #{expect}" do
subject { #source }
#source.name.should == expect
let!(:find) do
Source.find_by_proper_name(expect)
end
it "should find" do
should == find
end
end
end
end
end
I have a unique constraint on the name column of the source model.
Every iteration of the test comes back as a failure due to unique violation. (even though I thought it would create only one model per iteration.
If I take out the middle of the three so all names are unique if it creates all at once - then the tests fail because #source is equal to the last value in all tests.
Essentially I want a DRY way to write the tests
Source.create(name: "bob").name.should == "Bob"
Source.create(name: "edward jones").name.should == "Edward Jones"
Source.create(name: "Edward jones").name.should == "Edward Jones"
How should I go about it?
you are mixing a lot of stuff in there...
first things first:
do not nest it
do not use let in it they belong in context
do not iterate its iterate in the it
do not cleanup the db yourself, use database cleaner or transactional fixtures or something like that
have a look at how i do spec like these, maybe this helps you somehow: https://github.com/phoet/on_ruby/blob/master/spec/models/user_spec.rb#L15

How to DRY (these) RSpec tests using custom matcher

I currently have the following tests, which look like good candidates for a little DRY treatment:
describe League do
context 'attributes validation' do
before(:each) do
#league = League.new
end
it 'should be invalid without a short_name' do
#league.attributes = valid_league_attributes.except(:short_name)
#league.should_not be_valid
#league.should have(1).error_on(:short_name)
#league.errors[:short_name].should == ["can't be blank"]
#league.short_name = 'NFL'
#league.should be_valid
end
it 'should be invalid without a long_name' do
#league.attributes = valid_league_attributes.except(:long_name)
#league.should_not be_valid
#league.should have(2).error_on(:long_name)
#league.errors[:long_name].should == ["can't be blank", 'is not included in the list']
#league.long_name = 'National Football League'
#league.should be_valid
end
end
end
Is it possible to make this more DRY using Custom Matchers or some other utility?
It is possible, but I wouldn't recommend it. These two tests are sufficiently different that writing a method to wrap them into introduces more complexity than seems justified, and will make troubleshooting harder if one of the two tests should ever fail.
You might want to have a look at shoulda
This would allow you to write
describe League do
subject {League.new}
it {should validate_presence_of(:long_name)}
it {should validate_presence_of(:short_name)}
end
There's a bunch of other matchers for validations and associations too.

Referencing associations with Ruleby on Rails

I've recently started messing with a gem called 'ruleby', a rule-engine for Ruby. The documentation for ruleby is a bit sparse, however, and I can't seem to figure out how to properly reference associations for the rule-writing bit. I'm stumped both the 'pattern' part of the rule and also in the executing block part of the rule.
For example, let's say I had a rule which would only be executed only when a user submitted a positive review. I could, for instance, write the following:
rule :positive_review, [Review, :review, method.review_rating == "positive"] do |v|
assert (store positive_review somehow)
end
So it's at this point that I get lost. I would like to write a rule which would reference back to the user and check the total number of positive reviews that the user of this positive review and possibly execute certain actions based on this number.
If anyone could point me in the right direction, I would be greatly appreciative. Thanks.
I dont quite understand why you are asserting a fact inside of a rule, that should already be done. The github example code probably answered you by now but in case not:
First you initialise the engine, I did it like this so I could call on it more than once.
rule_engine = Ruleby::Core::Engine.new
RULES_ENG = RulesEngineCaller.new(rule_engine)
EngineRulebook.new(rule_engine).rules
Then you assert your facts
#events = Event.all
#events.each do |event|
#rule_engine.assert event
end
Then you run your engine
#rule_engine.match
if you want to add events you can just by calling assert per fact to the existing engine, it will just add to it until you either delete inside the engine or rebuild it.
The rulebook is where you add your facts for me I did it programatically:
class EngineRulebook < Rulebook
def rules
Rails.logger.debug "#{Time.now.utc} - Loading Rules into Rulebook"
#msg = Notification.new()
#rules = Rule.all
#rules.each do |rule_name|
case
#hard coded rule - for auto cleaning
when (rule_name.title == "SYSTEM ADMIN - auto-clean delete rule") && (rule_name.group.title == "Notification Admin")
rule [Event, :m, m.terminate_flag == 1] do |v|
if v[:m].rules.include?(rule_name) == false
#output matched rule to console & logfile
Rails.logger.info "#{Time.now.utc} - match rule #{rule_name.id} #{rule_name.title} - #{v[:m].ticket_id} - #{v[:m].description}"
#add reference so rule doesn't fire again
#event = Event.find_by_id(v[:m].id)
#event.rules << rule_name
v[:m].rules << rule_name
modify v[:m]
#Retract v[:m] would remove the fact from the rules engine, need to remove all related facts though
#so dont use this as other rules may be requires as rules do not fire in order
end
end
end
I also did it as a case statement as I was loading rules programmatically. I also put a check in to say has this rule run before because each time you run the engine it will assess all facts, even ones that have already matched.
Hopefully you have an answer, if not hopefully this helped.

How can I test :inclusion validation in Rails using RSpec

I have the following validation in my ActiveRecord.
validates :active, :inclusion => {:in => ['Y', 'N']}
I am using the following to test my model validations.
should_not allow_value('A').for(:active)
should allow_value('Y').for(:active)
should allow_value('N').for(:active)
Is there a cleaner and more through way of testing this? I am currently using RSpec2 and shoulda matchers.
EDIT
After some looking around I only found, this probably an 'ok' way of testing this, shoulda does not provide anything for this and anyone who requires it can write their own custom matcher for it.(And probably contribute it back to the project). Some links to discussions that might be intresting:
Links which indicate to the above . Link 1 , Link 2
should_ensure_value_in_range This one comes close to what can be used, but only accepts ranges and not a list of values. Custom matcher can be based on this.
Use shoulda_matchers
In recent versions of shoulda-matchers (at least as of v2.7.0), you can do:
expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N])
This tests that the array of acceptable values in the validation exactly matches this spec.
In earlier versions, >= v1.4 , shoulda_matchers supports this syntax:
it {should ensure_inclusion_of(:active).in_array(%w[Y N]) }
If you have more elements to test than a boolean Y/N then you could also try.
it "should allow valid values" do
%w(item1 item2 item3 item4).each do |v|
should allow_value(v).for(:field)
end
end
it { should_not allow_value("other").for(:role) }
You can also replace the %w() with a constant you have defined in your model so that it tests that only the constant values are allowed.
CONSTANT = %w[item1 item2 item3 item4]
validates :field, :inclusion => CONSTANT
Then the test:
it "should allow valid values" do
Model::CONSTANT.each do |v|
should allow_value(v).for(:field)
end
end
I found one custom shoulda matcher (in one of the projects I was working on) which attempts to coming close to test something like this:
Examples:
it { should validate_inclusion_check_constraint_on :status, :allowed_values => %w(Open Resolved Closed) }
it { should validate_inclusion_check_constraint_on :age, :allowed_values => 0..100 }
The matcher tries to ensure that there is a DB constraint which blows up when it tries to save it.I will attempt to give the essence of the idea. The matches? implementation does something like:
begin
#allowed_values.each do |value|
#subject.send("#{#attribute}=", value)
#subject.save(:validate => false)
end
rescue ::ActiveRecord::StatementInvalid => e
# Returns false if the exception message contains a string matching the error throw by SQL db
end
I guess if we slightly change the above to say #subject.save and let Rails validation blow up, we can return false when the exception string contains something which close matches the real exception error message.
I know this is far from perfect to contributed back to the project, but I guess might not be a bad idea to add into your project as a custom matcher if you really want to test a lot of the :inclusion validation.

Skip an RSpec test case at runtime

I'm running RSpec tests against a website product that exists in several different markets. Each market has subtly different combinations of features, etc. I would like to be able to write tests such that they skip themselves at runtime depending on which market/environment they are being run against. The tests should not fail when run in a different market, nor should they pass -- they're simply not applicable.
Unfortunately, there does not seem to be an easy way to mark a test as skipped. How would I go about doing this without trying to inject "pending" blocks (which aren't accurate anyway?)
Use exclusion filters.
describe "market a", :market => 'a' do
...
end
describe "market b", :market => 'b' do
...
end
describe "market c", :market => 'c' do
...
end
RSpec.configure do |c|
# Set these up programmatically;
# I'm not sure how you're defining which market is 'active'
c.filter_run_excluding :market => 'a'
c.filter_run_excluding :market => 'b'
# Now only tests with ":market => 'c'" will run.
end
Or better still, use implicit filters.
describe "market a", :if => CurrentMarket.a? do # or whatever
...
end
I spotted this question looking for a way to really skip examples that I know are going to fail now, but I want to "unskip" them once the situation's changed. So for rspec 3.8 I found one could use skip inside an example just like this:
it 'is going to fail under several circumstances' do
skip("Here's the reason") if conditions_met?
expect(something)
end
Actually, in most situations (maybe not as complicated as in this question) I would rather use pending instead of skip, 'cause it will notify me if tests stop failing, so I can "unskip" them. As we all know that skipped tests will never be performed ever again =)