I'm trying to make rspec testcases. But, Rspec fails for Name has already been taken.
It seems "let" evalulated each time "product" called.
How can I fix it?
Console
./spec/models/spree/product_decorator_spec.rb:31:in `block (4 levels) in <top (required)>'
ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
./spec/models/spree/product_decorator_spec.rb:6:in `block (3 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:20:in `block (4 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:23:in `block (4 levels) in <top (required)>'
ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
./spec/models/spree/product_decorator_spec.rb:6:in `block (3 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:12:in `block (4 levels) in <top (required)>'
./spec/models/spree/product_decorator_spec.rb:15:in `block (4 levels) in <top (required)>'
product_decorator_spec.rb
require 'spec_helper'
describe Spree::Product do
context '#create' do
let(:us) { create(:zone, name: "US") }
let(:china) { create(:zone, name: "China") }
let(:japan) { create(:zone, name: "Japan") }
context "when a product has no ng zone" do
let(:product) { create(:product, zones: [us, china, japan]) }
it "should get ng_zones correctly" do
product.ng_zones.should match_array []
end
end
context "when a product has one ng zone" do
let(:product) { create(:product, zones: [us, china]) }
it "should get ng_zones correctly" do
product.ng_zones.should match_array ["Japan"]
end
end
context "when a product has two ng zone" do
let(:product) { create(:product, zones: [us]) }
it "should get ng_zones correctly" do
product.ng_zones.should match_array ["China", "Japan"]
end
end
end
end
The body of the let is evaluated with every it block. I'm assuming you have a uniqueness constraint on your Zone classes.
You have 2 possibilities
Either build the variables in a before(:all) block and assign them to something like #us
Clean up your database after(:each)
You are testing the Product#create so, you won't need to really create zones for this particular test.
Instead you could just use build_stubbed method.
let(:us) { build_stubbed(:zone, name: "US") }
let(:china) { build_stubbed(:zone, name: "China") }
let(:japan) { build_stubbed(:zone, name: "Japan") }
This way, it's creation process are not going to rely on the database and also the validations for the Zone model and you have an awesome performance boost since you are not hitting the db for for each single test.
You can read about build_stubbed here.
Please let me know if it helps you somehow. ;)
Related
I have an almost meaningless worker test spec:
require 'rails_helper'
require 'sidekiq/testing'
Sidekiq::Testing.fake!
RSpec.describe FetchArticleContentWorker, type: :worker do
describe "Sidekiq Worker" do
before(:each) do
allow_any_instance_of(DataSource).to receive(:subscribe).and_return(true)
allow_any_instance_of(FetchArticleContentWorker).to receive(:perform).and_return(true)
end
let(:user) { FactoryBot.create(:user) }
let(:data_source) { FactoryBot.create(:data_source) }
let(:article) { FactoryBot.create(:article, data_source_id: data_source.id) }
it "should respond to #perform" do
expect(FetchArticleContentWorker.new).to respond_to(:perform)
end
describe "fetch article content worker" do
before do
Sidekiq::Worker.clear_all
end
it "increase the worker job size" do
expect { FetchArticleContentWorker.perform_async(article.url) }.to change(FetchArticleContentWorker.jobs, :size).by(1)
end
it "assert that jobs were pushed on to the queue" do
assert_equal 0, FetchArticleContentWorker.jobs.size
FetchArticleContentWorker.perform_async(article.url)
assert_equal 1, FetchArticleContentWorker.jobs.size
end
it "job runs successfully" do
expect(FetchArticleContentWorker.new.perform(article.url)).to eq(true)
end
end
end
end
For whatever reason, this set of specs has suddenly started failing but only when run as a part of the entire test suite. If I run this test directly by itself, it passes every time.
When run with bundle exec rspec (to do the whole suite), I get the following failures for this particular test:
Failures:
1) FetchArticleContentWorker Sidekiq Worker fetch article content worker increase the worker job size
Failure/Error: expect { FetchArticleContentWorker.perform_async(article.url) }.to change(FetchArticleContentWorker.jobs, :size).by(1)
expected `Array#size` to have changed by 1, but was changed by 0
# ./spec/workers/fetch_article_content_worker_spec.rb:29:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:48:in `block (3 levels) in <top (required)>'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:47:in `block (2 levels) in <top (required)>'
2) FetchArticleContentWorker Sidekiq Worker fetch article content worker assert that jobs were pushed on to the queue
Failure/Error: assert_equal 1, FetchArticleContentWorker.jobs.size
Minitest::Assertion:
Expected: 1
Actual: 0
# /usr/local/bundle/gems/minitest-5.14.0/lib/minitest/assertions.rb:183:in `assert'
# /usr/local/bundle/gems/minitest-5.14.0/lib/minitest/assertions.rb:218:in `assert_equal'
# ./spec/workers/fetch_article_content_worker_spec.rb:35:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:48:in `block (3 levels) in <top (required)>'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/generic/base.rb:16:in `cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:87:in `block (2 levels) in cleaning'
# /usr/local/bundle/gems/database_cleaner-1.8.4/lib/database_cleaner/configuration.rb:88:in `cleaning'
# ./spec/rails_helper.rb:47:in `block (2 levels) in <top (required)>'
It appears that these worker specs only fail when a feature using turnip is previously run.
I ended up finding what I think is the problem.
In my turnip steps file I was setting sidekick to inline:
# set sidekiq to inline
require 'sidekiq/testing'
Sidekiq::Testing.inline!
And, even though I was setting Sidekiq::Testing.fake! in the above mentioned spec file, it didn't seem to "take".
When I moved fake! into the before block, that seemed to fix the problem:
describe "Sidekiq Worker" do
before(:each) do
allow_any_instance_of(DataSource).to receive(:subscribe).and_return(true)
allow_any_instance_of(FetchArticleContentWorker).to receive(:perform).and_return(true)
Sidekiq::Testing.fake!
end
...
I am not sure why this is the fix, but this fixed it.
I'm learning Ruby on rails using http://railstutorial.org/ . While going through chapter 6 in his book. I came across my user fields becoming nil. Which in turn causes the authenticate method to fail.
FYI I have used rails 5 and ruby 2.6 all lastest Gems unlike the one mentioned in the tutorial.
This is Rspec results after running the tests
$bundle exec rspec spec/
including Capybara::DSL in the global scope is not recommended!
Randomized with seed 55216
............F..........FFF..F...
Failures:
1) User should be valid
Failure/Error: it { should be_valid }
expected #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil> to be valid, but got errors: Password can't be blank
# ./spec/models/user_spec.rb:27:in `block (2 levels) in <top (required)>'
2) User return value of authenticate method with valid password should When you call a matcher in an example without a String, like this:
specify { expect(object).to matcher }
or this:
it { is_expected.to matcher }
RSpec expects the matcher to have a #description method. You should either
add a String to the example this matcher is being used in, or give it a
description method. Then you won't have to suffer this lengthy warning again.
Failure/Error: it { should == found_user.authenticate(#user.password) }
NoMethodError:
undefined method `authenticate' for nil:NilClass
# ./spec/models/user_spec.rb:64:in `block (4 levels) in <top (required)>'
3) User return value of authenticate method with invalid password
Failure/Error: let(:user_for_invalid_password) { found_user.authenticate("invalid") }
NoMethodError:
undefined method `authenticate' for nil:NilClass
# ./spec/models/user_spec.rb:68:in `block (4 levels) in <top (required)>'
# ./spec/models/user_spec.rb:71:in `block (4 levels) in <top (required)>'
4) User return value of authenticate method with invalid password should not When you call a matcher in an example without a String, like this:
specify { expect(object).to matcher }
or this:
it { is_expected.to matcher }
RSpec expects the matcher to have a #description method. You should either
add a String to the example this matcher is being used in, or give it a
description method. Then you won't have to suffer this lengthy warning again.
Failure/Error: let(:user_for_invalid_password) { found_user.authenticate("invalid") }
NoMethodError:
undefined method `authenticate' for nil:NilClass
# ./spec/models/user_spec.rb:68:in `block (4 levels) in <top (required)>'
# ./spec/models/user_spec.rb:70:in `block (4 levels) in <top (required)>'
5) User when email format is valid should be valid
Failure/Error: #user.should be_valid
expected #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil> to be valid, but got errors: Password can't be blank
# ./spec/models/user_spec.rb:96:in `block (4 levels) in <top (required)>'
# ./spec/models/user_spec.rb:94:in `each'
# ./spec/models/user_spec.rb:94:in `block (3 levels) in <top (required)>'
Deprecation Warnings:
Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }` instead. Called from /home/user/rails_projects/sample_app/spec/models/user_spec.rb:96:in `block (4 levels) in <top (required)>'.
If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.
1 deprecation warning total
Finished in 0.49086 seconds (files took 0.54848 seconds to load)
32 examples, 5 failures
Failed examples:
rspec ./spec/models/user_spec.rb:27 # User should be valid
rspec ./spec/models/user_spec.rb:64 # User return value of authenticate method with valid password should When you call a matcher in an example without a String, like this:
specify { expect(object).to matcher }
or this:
it { is_expected.to matcher }
RSpec expects the matcher to have a #description method. You should either
add a String to the example this matcher is being used in, or give it a
description method. Then you won't have to suffer this lengthy warning again.
rspec ./spec/models/user_spec.rb:71 # User return value of authenticate method with invalid password
rspec ./spec/models/user_spec.rb:70 # User return value of authenticate method with invalid password should not When you call a matcher in an example without a String, like this:
specify { expect(object).to matcher }
or this:
it { is_expected.to matcher }
RSpec expects the matcher to have a #description method. You should either
add a String to the example this matcher is being used in, or give it a
description method. Then you won't have to suffer this lengthy warning again.
rspec ./spec/models/user_spec.rb:92 # User when email format is valid should be valid
Randomized with seed 55216
Project
sample_app/tree/modelling_user
Files
spec/models/user_spec.rb
app/models/user.rb
Gemfile
I have the following feature tests passing:
require 'rails_helper'
describe "Create a quetsion", type: :feature do
let(:question) { build(:question) }
before do
login_as create(:user, :teacher)
exercise = create(:exercise)
visit new_exercise_question_url(exercise.id)
end
context "whit valid attributes" do
subject do
fill_in "question_score", with: question.score
fill_in "question_description", with: question.description
click_on "Criar"
end
it "create the question" do
expect{ subject }.to change(Question, :count).by(1)
expect(page).to have_current_path(question_path(Question.first.id))
end
end
context "whit invalid attributes" do
subject do
click_on "Criar"
end
it "doesn't create the exercise" do
expect{ subject }.to change(Question, :count).by(0)
expect(page).to have_selector("div.alert.alert-danger")
end
end
end
That works fine, unless I add js: true. In this case, I have the following errors:
1) Create a quetsion whit valid attributes create the question
Failure/Error: fill_in "question_score", with: question.score
Capybara::ElementNotFound:
Unable to find field "question_score"
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:44:in `block in find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/base.rb:85:in `synchronize'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:33:in `find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/actions.rb:85:in `fill_in'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:735:in `block (2 levels) in <class:Session>'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (4 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (3 levels) in <top (required)>'
2) Create a quetsion whit invalid attributes doesn't create the exercise
Got 0 failures and 2 other errors:
2.1) Failure/Error: visit new_exercise_question_url(exercise.id)
Capybara::Poltergeist::StatusFailError:
Request to 'http://www.example.com/exercises/1/questions/new' failed to reach server, check DNS and/or server status
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:376:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:35:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:97:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:240:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:8:in `block (2 levels) in <top (required)>'
2.2) Failure/Error: #socket.send(command.id, command.message, receive_timeout) or raise DeadClient.new(command.message)
Capybara::Poltergeist::DeadClient:
PhantomJS client died while processing {"id":"2928bf26-2dd8-45d7-8822-41b2923fd40d","name":"reset","args":[]}
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/server.rb:38:in `send'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:369:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:224:in `reset'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:183:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:109:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `block in reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reverse_each'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/rspec.rb:21:in `block (2 levels) in <top (required)>'
I checked, whit js: true my page html isn't written too (instead the desired HTML, I only receive <html><head></head><body></body></html>).
I added poltergeist gem to test javascritp, and configured it in my rspec rails_helper.rb file:
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
PhantomJS is already instaled and available in my $PATH. I'm completelly out of ideas, what can be happening here?
There are a number of potential issues here.
If you have set Capybara.server = :puma, make sure you're not running puma 3.7.0 which has a bug in it that will be fixed when 3.7.1 is released. For now use 3.6.9
If this is the first time you're setting up js tests make sure you have setup and correctly configured DatabaseCleaner so that you are no running in transaction mode for js: true tests. See https://github.com/teamcapybara/capybara#transactions-and-database-setup and https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example for the recommended configuration
Once you have #1 and #2 worked out then you'll need to look at your tests and deal with the fact that after a click/click_on type action you need to check for a visible page change to ensure the action has completed before you move on. This is because clicks happen but the actions those clicks trigger can occur asynchronously. In your current example that means you would need something like
...
click_on "Criar"
expect(page).to have_text("Question created!") #whatever message is shown when the action has completed, or check the page etc.
Without something like that the change check will fail because it will check the count before the actual actually occurs.
Default to _path helpers rather than _url helpers when there's no need for a specific host name (99.9% of the time). This lets Capybara fill_in the correct host/port for the server being run. To visit _url helpers there are a number of configuration parameters that need to be carefully set.
Note: It's generally not best practice to be checking database counts in feature tests, they should generally be limited to checking on visible page changes.
I have problems with error message ./spec/models/lib/parsers/s_reality_cz/matcher_spec.rb:12:in `block (2 levels) in ' in every rspec test. What I'm doing wrong? Using rspec-rails 3.5. Thanks
Code:
require 'rails_helper'
RSpec.describe Parsers::SRealityCz::Matcher, :type=> :model do
before do
#doc = File.open("spec/fixtures/srealitycz_profile.html") { |f| Nokogiri::HTML(f) }
end
let(:parser) { described_class }
it "returns total price" do
expect(parser.title(#doc)).to eq "Prodej bytu 1+kk 40 m²"
end
end
Rspec output:
Randomized with seed 37464
expected: "Prodej bytu 1+kk 40 m²"
got: "Prodej bytu 1+kk 40 m²"
(compared using ==)
./spec/models/lib/parsers/s_reality_cz/matcher_spec.rb:12:in `block (2 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'
1 example, 1 failure, 0 passed
Finished in 0.139173354 seconds
Your strings look the same, but they probably have different encodings.
Convert them both to the same encoding (e.g. UTF-8), and compare them.
I recently changed laptops and now Rspec doesn't seem to run normally locally.
When I run a simple test:
describe 'price' do
let(:product) { create(:product) }
let(:product_item) { create(:product_item, product_id: product.id) }
it "is half of the retail price if it has never been rented" do
product.update_attributes(sale_price_new: [200_000, 190_000])
product_item.reload
expect(product_item.price(2.days.from_now, "EUR")).to eq(100_000)
end
I get
Failures:
1) ProductItem price is half of the retail price if it has never been rented
Failure/Error: let(:product) { create(:product) }
RestClient::RequestTimeout:
Request Timeout
# ./spec/models/product_item_spec.rb:108:in `block (3 levels) in <top (required)>'
# ./spec/models/product_item_spec.rb:160:in `block (3 levels) in <top (required)>'
# ./spec/spec_helper.rb:85:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# IO::EAGAINWaitReadable:
# Resource temporarily unavailable - read would block
# ./spec/models/product_item_spec.rb:108:in `block (3 levels) in <top (required)>'
But when I run it on the server, or in the previous laptop, everything works fine.
Anyone knows what I'm missing?
Thank you so much!