In the railstutorial, why does the author choose to use this (Listing 10.25):
http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-users
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
User.create!(:name => "Example User",
:email => "example#railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}#railstutorial.org"
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
to populate the database with fake users, and also (Listing 7.16)
http://ruby.railstutorial.org/chapters/modeling-and-viewing-users-two
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
It appears that both ways creates users in the database right (does factory girl create users in the database)? What is the reason for the two different ways of creating test users, and how are they different? When is one method more suitable than the other?
Faker and Factory Girl are being used in those examples for two different purposes.
A rake task is created using Faker to easily let you populate a database, typically the development database. This lets you browse around your app with lots of populated, fake data.
The factory definition makes tests convenient to write. For example, in your RSpec tests you can write:
before(:each) do
#user = Factory(:user)
end
Then #user is available in the tests that follow. It will write these changes to the test database, but remember that these are cleared each time you run tests.
Related
So I want to make sure this is possible and actually doable with what im trying to do.
I had previously been using a seeds.rb file to seed the database with test data, this worked for my Capybara integration tests but messed up some of the other unit tests which rely on a schema.rb file and Factories to create data.
Can I use a factory to create test data for that particular integration spec. Something like this for a factory:
FactoryGirl.define do
factory :user do
first_name Faker::Name.first_name
last_name Faker::Name.last_name
email Faker::Internet.email
password "password"
end
end
Then I have a super simple spec which logins to a site in Capybara:
it 'Check correct login' do
visit('/sign_in')
page.fill_in 'user_email', :with => 'test#test.com'
page.fill_in 'user_password', :with => 'p4ssw0rd'
click_button('Sign In')
end
This is hitting a Sqlite database, can I insert my factory and it will work using capybara? Im really not familiar with factorygirl, but is it actually creating data in the database and then just removing it?
I don't commonly see Capybara used with FactoryGirl in a lot of the examples I search online, is there a reason for that?
edit:
Here is what I have currently:
it 'Check correct login' do
user = FactoryGirl.create(:user)
visit('/d/users/sign_in')
page.fill_in 'user_email', :with => user.email
page.fill_in 'user_password', :with => user.password
click_button('Log In')
page.assert_text('Signed in successfully.')
end
I was able to take a screenshot and confirm that the forms are getting filled in with a random username/password, but using DB Browser for SQlite I don't seem to see the actual data in the database getting populated (Which might be difficult since once the spec is done it's deleted right?) but I can't tell if it's actually creating the data or not.
Thanks!
This should work fine w/o many hitches. You'll need to instantiate your User object in your test as this looks like a 'sign-in'.
You can create the object a couple of different ways.
(1)
describe 'Test' do
let(:user){ create :user }
...
it 'Specific Test' do
...
Note that let!(:user){...} creates immediately and let(:user){...} will only create user when the user is called. You will then be able to access user in all your tests.
(2)
describe 'Test' do
before do
#user = FactoryGirl.create( :user )
end
it 'Specific Test' do
...
Here you will be able to access #user in all your tests.
Let us know if that works and if not what error you're getting.
EDIT:
If your FG file looked like this:
FactoryGirl.define do
factory :user do
first_name Faker::Name.first_name
last_name Faker::Name.last_name
email Faker::Internet.email
password "password"
work
end
end
You could associate work like this:
let(:user){ create :user, work: 'Some Place' }
#user = FactoryGirl.create(:user, work: 'Some Place')
I am trying to populate my db using rake db:populate. I am on chapter 10.3.2 on michael hartl's book.
Even though I don't get any error messages the DB doesn't seem to be populating.
This is the sample_data.rake file I created:
namespace :db do desc "Fill database with sample data" task populate: :environment do
User.create!(:name => "Example User",
:email => "example#railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}#railstutorial.org"
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
Stopping your server before resetting and repopulating your db should work.
I've been trying to get a grasp on writing tests, but having a lot of trouble as the tests never seem to validate the way I want them to. In particular, I've been trying to use Factory Girl as opposed to fixtures - as suggested by a recent Railscasts and other advice I've seen on the net - due to the benefits it professes, and that hasn't worked out.
For example, here is a simple Rspec test for a user model, testing to make sure a username is present...
describe User do
it "should not be valid without a username" do
user = FactoryGirl.create(:user, :username => "", :password => "secret")
user.should_not be_valid
end
end
And my factories.rb file, if it helps...
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "registered-#{n}" }
password "foobar"
end
end
When I run 'rake spec,' it tells me...
1) User should not be valid without a username
Failure/Error: user = FactoryGirl.create(:user, :username => "", :password => "secret")
ActiveRecord::RecordInvalid:
Validation failed: Username can't be blank
Ummm...that's the POINT. If I specified that the user should NOT be valid, shouldn't this test actually pass?
If I replace the Factory Girl line and set the user in the test with something like 'user = User.new(:username => "", :password => "secret")', to no surprise the test passes fine. So why is Factory Girl not working right?
You should use build like in the following:
user = Factory.build(:user, :username=>"foo")
Because using the method you're using will try to create a record. See docs for further information.
Here's the test:
describe "admin attribute" do
before(:each) do
#user = User.create!(#attr)
end
it "should respond to admin" do
#user.should respond_to(:admin)
end
it "should not be an admin by default" do
#user.should_not be_admin
end
it "should be convertible to an admin" do
#user.toggle!(:admin)
#user.should be_admin
end
end
Here's the error:
1) User password encryption admin attribute should respond to admin
Failure/Error: #user = User.create!(#attr)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/user_spec.rb:128
I'm thinking the error might be somewhere in my data populator code:
require 'faker'
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
admin = User.create!(:name => "Example User",
:email => "example#railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
admin.toggle!(:admin)
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}#railstutorial.org"
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
Please let me know if I should reproduce any more of my code.
UPDATE: Here's where #attr is defined, at the top of the user_spec.rb file:
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
Check to be sure that there isn't a block further up your user_spec.rb that is calling User.create in a before(:each) block with the same email address. If your blocks are nested incorrectly, you'll get this error. For example, in the Rails tutorial, it's easy to accidentally nest your describe "admin attribute" inside your describe "password encryption" block, which uses the same before(:each) code.
Try checking for existing users in the before block:
before(:each) do
User.count.should == 0
#user = User.create!(#attr)
end
If that fails, then another user exists with the same email. This could be because another before block created a user with the same attributes, or that the test database was not correctly cleaned out after a failure. For the latter case, try running rake db:test:prepare, and then run the spec again.
before( :each ) is going to create a new user object from #attr. So if #attr isn't changing the values for its fields, and you have validations turned on to prevent duplicate, then on your 2nd test, the user object you created in the first test will collide with the one you are trying to create in the 2nd test.
There are other ways to go about testing your model without the database. For example, you can use test doubles to create and setup objects with exactly the data you want and then run your test to see if it behaves correctly. There is a [great book on RSpec, Cucumber and BDD] that could be a great source.
Edit: My apologies, I was confusing before(:each) with before(:all).
This does not seems to be ideal way of setting up test data. ie, using a rake task to populate the database.
A more standard unit testing and Rails practice would be to use both factory_girl or a test_fixture and transactional test fixture or database_cleaner gem.
Read a little bit about those, and they should be straight forward to use. They ensure, that each of your rspec test runs in isolation even when you run all of them together. That way, each test data for one test will not affect the other one.
I am using faker to generate sample data. I have this as follows:
require 'faker'
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
User.create!(:name => "rails",
:email => "example#railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
99.times do |n|
#name = Faker::Name.name
name = "rails#{n+1}"
email = "example-#{n+1}#railstutorial.org"
password = "password"
user = User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
The problem is that I have a couple of after_save callbacks that are not being called when the User is created. Why is that? Thanks
The methods:
after_save :create_profile
def create_profile
self.build_profile()
end
In all my reading, it seems that save! bypasses any custom before_save, on_save or after_save filters you have defined. The source code for create! reveals that it invokes save!. Unless you absolutely NEED the bang version, why are you using it? Try removing all your bang methods and just invoking the non-bang versions:
[1..100].each do |n|
name = "rails#{n+1}"
email = "example-#{n+1}#railstutorial.org"
password = "password"
user = User.new(:name => name, :email => email, :password => password, :password_confirmation => password)
if !user.save
puts "There was an error while making #{user.inspect}"
end
end