Dependent Tests in Rspec

Dependent tests in rspec

RSpec is designed as a unit tests framework, so it may be a little difficult to get perfect functional-test behavior from it. In RSpec's philosophy tests must be independent. It is especially important when you use autotest: in this case the order of execution is truly unpredictable. Sad but true.

Still, of course you can save some state between test using global ($a) or instance (@a, not sure here) variables. Anyway you need to move if into it block so that it will be executed in time. You may use pending keyword to interrupt a test without failing it in case if pre-condition is not met.

BUT

I'm sure that the best solution is to avoid golden hammer antipattern, and not to write functional tests in unit-test framework. You want not to test some separate functions. You do want to test scenarios. So I suggest trying some scenario-testing suite.

Behold, Cucumber! Usage is simle:

  1. Define parametrized scenario steps in Ruby, and expectations in RSpec-style
  2. Write scenarios in natural language (yes, not only English, but even Russian or whatever — the power of Regexps is on your side)

In your case, you will have in features/step_definitions/gui_steps.rb something like

Given /I pushed a button "(.*)"/ do |name|
@buttons.find(name).click() # This line is pseudo-code, you know
end

and something similar for checking window opening, etc (see examples). Then you can combine defined steps in any way, for example your two scenarios might look like

Scenario: Feature 1
Given I pushed a button "go"
And I focus on opened window
When I trigger "feature 1"
Then I should se "result 1" in text area

Scenario: Feature 2
Given I pushed a button "go"
And I focus on opened window
When I trigger "feature 2"
Then I should se "result 2" in text area

In both cases, if some step of scenario fails (like I focus on opened window — if it is not opened), the consequent steps are not executed — just as you want. As a bonus you get an extremely detailed output of what happened and on what step (see pics on the site).

The good news is that you don't always need to define all the step yourself. For example, when you test a web app, you can use webrat steps for typical things like When I go to url/a/b/c and Then I should see text "foo" on the page. I don't know which GUI testing framework you use, but probably there are already steps for it, so I suggest you to google on Cucumber %framework name%. Even if not, writing these steps once will not be more difficult than trying to make Cucumber from RSpec.

How to test dependent: :destroy with RSpec?

It is the right matcher, but you're not using it the correct way:

  • expect needs to receive a block containing the action to perform (in your case deleting the user)
  • change needs to receive a block that produces the numerical value that is expected to change (it can also receive an object and a symbol indicating that rspec should call the named method)

The correct way is

expect { user.destroy }.to change { Project.count }

This just asserts that the numerical value changes, but does not specify by how much. To do that, chain a call to by:

expect { user.destroy }.to change { Project.count }.by(-1)

Can I realize dependency injection in my RSpec tests?

Finally I solved my problem using lambda:

RSpec.shared_examples "changing status" do |arguments|
/* ... */

it_behaves_like "action requiring proper user logged in to be successful", action, -> {
before(:each) do
/* ... */
end
context "and when the reservation has an allowed status" do
/* ... */
end
context "and when the reservation has a not allowed status" do
/* ... */
end
}
end
RSpec.shared_examples "action requiring proper user logged in to be successful" do |action, successful_behavior|
context "- when the required logged user is logged in" do
successful_behavior.()
end
context "- when the logged in user is not the required logged user" do
before(:each) do
login(incidental_user)
end
it_behaves_like "unsuccessful attempt to change the reservation", action
end
context "- when there's no user logged in" do
it_behaves_like "unsuccessful attempt to change the reservation", action
end
end

Testing dependent models with rspec

FactoryGirl works as a replacement for fixtures. Thus you don't use the fixtures method with FactoryGirl, rather it has its own DSL:

FactoryGirl.create(:product) # Saves a product to the database
FactoryGirl.build_stubbed(:product) # Builds a product and makes it look look like it has been persisted
FactoryGirl.attributes_for(:product)
# etc

You can setup shortcuts by including FactoryGirl::Syntax::Methods in your specs:

RSpec.configure do |config|
# ..
config.include FactoryGirl::Syntax::Methods
# ..
end

You then use the factories in your specs with rspecs let and let! methods.

describe "Let methods and factories" do

let(:product) { create(:product) } # initialized when it is used
let!(:foo) { create(:foo) } # initialized before any specs are run

describe "let" do
expect(product).to be_a Product
end
end

The database_cleaner gem is invaluable for cleaning up inbetween specs avoid any test ordering issues or false positives.

In your case you may want to just use the factory straight off for each test case.

describe Product do

# It is a good practice to put examples in a describe block
# which tells which method we are testing
describe "#create_document" do

it 'returns title' do
product = build_stubbed(:product, title: build_stubbed(:title))
expect(product.create_document()[:title]).to be_a Title
end

it 'returns title and category' do
product = build_stubbed(:product, category: build_stubbed(:category))
expect(product.create_document()[:category]).to be_a Category
end
#..
end
end

How do you test a model that is dependent on another?

You test objects that depend on other objects by using a mocking framework to "mock" the other objects. There are a number of mock frameworks that allow you to do this.

Is there a good way to debug order dependent test failures in RSpec (RSpec2)?

Found my own question 4 years later, and now rspec has a --order flag that lets you set random order, and if you get order dependent failures reproduce the order with --seed 123 where the seed is printed out on every spec run.

https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/command-line/order-new-in-rspec-core-2-8



Related Topics



Leave a reply



Submit