FactoryGirl.create not increasing `.count` field - ruby-on-rails

I had the same problem as Nick Rutten in this thread Rails Tutorial: RSpec test decoupling. I suspected the problem was the FactoryGirl.create() saving to the database, and thanks to #prusswan I fixed it.
However I do not understand why these puts don't indicate different User.count.
This is the snippet I'm interested in:
describe "with valid information" do
puts "Number of users pre-Factory:" + User.count.to_s
let(:user){FactoryGirl.create(:user)}
puts "Number of users post-Factory:" + User.count.to_s
(...)
end
I get:
Number of users pre-Factory:0
Number of users post-Factory:0
But shouldn't I get instead?:
Number of users pre-Factory:0
Number of users post-Factory:1
Btw, the factory is defined (although not so relevant now) as:
FactoryGirl.define do
factory :user do
name "Example User"
email "user#example.com"
password "foobar"
password_confirmation "foobar"
end
end
I'm stuck trying to understand why the counter is not increasing, wasn't the problem that it did increase to start with?
thank you
Edit using #derekyau's suggestions:
Right now I've got in this order:
it "print counter before let"
puts "Number of users:" + User.count.to_s
end
let(:user){FactoryGirl.create(:user)}
it "print counter after let"
puts "Number of users:" + User.count.to_s
end
However now I get both values 1:
Number of users:1
Number of users:1
Close but not there yet!
Edit 2
As explained by #Peter Alfvin here: Within a given RSpec describe block, does the order of let, before and it statements matter? . The order of let, before, it is pretty much established, regardless of the code. That, together with #derekyau's explanation closes the question.

Good question, I think that let(:user) is lazily evaluated so it isn't actually called until you use it inside of an it block.
If you want to force it to be evaluated try let!
let!(:user) {FactoryGirl.create(:user)}
If you want to see it change try doing the puts inside of an "it block"
it "check user count" do
puts User.count
end
You should see '1'

Related

rspec #variable returning nil

I have an issue with my #attributes variable. I would like it to be accessible to keep my code dry, but currently, I have to restate the variable and set it to "values" to get my rspec test to work. What is a better way to do this without duplicating the values.
ref: Unexpected nil variable in RSpec
Shows that it is not accessible in describe, but there needs be another solution. When would "specify" be appropriate? I have not used it.
describe "When one field is missing invalid " do
before(:each) do
#user = create(:user)
#attributes = {"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}
end
values = {"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}
values.keys.each do |f|
p = values.except(f)
it "returns invalid when #{f.to_s} is missing" do
cr = CarRegistration::Vehicle.new(#user, p)
cr.valid?
end
end
end
Update based on comments:
I would also like to use the values array hash in other tests. If I put it in the loop as stated, I would still have to repeat it in other places. Any other recommendations?
Update: I tried using let(),
describe "When one field is missing" do
let(:user) {Factorybot.create(:user)}
let(:attributes) = {{"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}}
attributes do |f|
p = attributes.except(f)
it "returns invalid when #{f.to_s} is missing" do
cr = CarRegistration::Vehicle.new(user, p)
cr.valid?
end
end
end
but get the following error.
attributes is not available on an example group (e.g. a describe or context block). It is only available from within individual examples (e.g. it blocks) or from constructs that run in the scope of an example (e.g. before, let, etc).
In either of your snippets, you don't need attributes inside of your specs. It is data to generate specs. As such, it must live one level above.
describe "When one field is missing" do
let(:user) { Factorybot.create(:user) }
attributes = { "has_car" => "true", "has_truck" => "true", "has_boat" => "true", "color" => "blue value", "size" => "large value" }
attributes do |f|
p = attributes.except(f)
it "returns invalid when #{f.to_s} is missing" do
cr = CarRegistration::Vehicle.new(user, p)
cr.valid?
end
end
end
As you seem to have recognized, based on the other SO post you linked to, you can't refer to your instance variables out in your describe block. Just set it as a local variable as you've done.
Using let
describe "When one field is missing" do
let(:user) {Factorybot.create(:user)}
let(:attributes) = {{"has_car"=>"true", "has_truck"=>"true", "has_boat"=>"true", "color"=>"blue value", "size"=>"large value"}}
## The variables are used INSIDE the it block.
it "returns invalid when a key is missing" do
attributes do |f|
p = attributes.except(f)
cr = CarRegistration::Vehicle.new(user, p)
expect(cr.valid?).to eq(true) # are you testing the expectation? Added this line.
end
end
end
Personally I don't like writing test (like the above) which could fail for multiple reasons. Sergio is correct. But if you want to use let you have to make use of it from WITHIN the it block - this example shows that.

the right way to change the associated object in rspec

I recently started to test with rspec, so I can strongly be mistaken, correct me if there is a better way
I create two related models
let(:user) {FactoryGirl.create :user}
let!(:participation) {FactoryGirl.create :participation, user: user}
and before one of the tests change one of the related objects
context "when" do
before {participation.prize = 100}
it "" do
binding.pry
end
end
But inside it
participation.prize => 100
user.participatons.select(:prize) => nil
what am I doing wrong ? and how to fix it?
When you say user.participations.select(:prize), you're making a query to the db to get values in the user's participations' prize columns. But when you say before {participation.prize = 100} you're only setting the prize attribute on the participation object. Try saving the participation before the select line:
participation.prize # => 100
participation.save
user.participatons.select(:prize) # => nil
Another possible issue is that user.participations has been memoized by a previous call. Ensure that user.participations.first == participation. If it doesn't, check
1) puts participation.user_id and
2) puts user.participations, user.reload.participations
Lastly, a better way of setting up the test so that you run into this issue less often is something along the lines of:
# let(:price) { 0 } # default price. Optional so that tests won't throw errors if you forget to set it in a context/describe block.
let(:user) {FactoryGirl.create :user}
let!(:participation) {FactoryGirl.create :participation, user: user, price: price}
# ...
context "when ..." do
let(:price) { 100 }
it "" do
binding.pry
end
end
This way, the price is set when you create the model. Following this pattern generally means running into this problem less.

Rspec - expected #count to have changed by 1, but was changed by 0

I have this feature:
feature "Blog features -", type: :feature, js: true do
let!(:admin) { FactoryGirl.create(:admin) }
scenario "Create new Blog" do
expect do
sign_in_as_admin(admin)
visit "/admin/blogs/new"
fill_in "blog_title", with: "title"
fill_in "blog_content", with: "lorem ipsum dolor"
click_button "Save"
end.to change(Blog, :count).by(1)
end
end
The Blog is saving correctly on the database but the test is not passing and I get this error: expected #count to have changed by 1, but was changed by 0
I don't know enough about your setup to determine if it's an error in the upstream code or if it's simply a race condition. My gut tells me you're racing. Try adding a sleep after the click save to see if that helps.
If your test is booting up a second process for your server (which I suspect it is) then your test is firing off a request (to be handled by the server at some future time) and then immediately checking the blog count. The request hasn't been handled by your server by the time you're checking the blog count.
Instead of checking the blog count at the database level, I'd recommend checking text or elements on the page. The user is getting some feedback from the save right? Assert against that.

How to use shared_examples_for in rsepc-mock?I have try many times but failed.who can help ,please? Wait online

I am a freshman on Rspec
it "should not ask the database" do
#sqlite_database.should_not_receive(:findISBN)
#result = #cache.findISBN('1234')
#result.should eql(#book)
end
it "should not ask the database" do
#sqlite_database.should_not_receive(:authorSearch)
#result = #cache.authorSearch('author')
#result.should eql(#book)
end
Here are two different part 1,:findISBN and :authorSearch 2 findISBN('1234') and authorSearch('author')
I try to use let but it doesn't work ,who can help ?
#sqlite_database = double()
#cache = SQLiteDataBaseWithCache.new(#sqlite_database)
That's ture ,I'm coming from java background .You coding showed some warning: syntax error, unexpected ':', expecting keyword_end (SyntaxError).I have no ideal about it
without more details on the variables like #sqlite_database, #cache etc its hard to tell whats happening, but following should work
probably a stack trace would help
following is the idea of shared examples :)
shared_examples "it should not ask database" do |symbol, params|
it "should not ask the database" do
#sqlite_database.should_not_receive(symbol)
#result = #cache.send symbol, params
#result.should eql(#book)
end
end
it_behaves_like "it should not ask the database", :findISBN, '1234'
it_behaves_like "it should not ask the database", :authorSearch, 'author'
and on a side note, your method signatures are not ruby kind of... in ruby we normally dont use camel case
so in ruby/rails its should be author_search, probably you are coming from java background :)

Check if attribute contains 2 letters in RSpec

What is the right way to test if the field contains 2 letter string with RSpec ? I am following an old example that I guess worked in rails 2. It creates new Address instance, sets invalid value on it, and then trigger valid? on that instance and finally checks if the errors report something wrong.
it 'requires state to be of length 2' do
subject = Address.new
subject.state = 'Cal'
should_not be_valid
subject.errors.on(:state).should_not be_nil
end
Now, Rails 3 doesn't have errors.on, so I tried with
subject.errors[:state].should_not be_nil
But the problem here is that errors[:attribute] is empty Array instead of nil.
You can still say
subject.errors[:state].should_not be_empty
Validation errors are now in errors.messages
errors.messages.should be_present

Resources