So it seems that I must be doing this wrong.
Task.create :name => 'apples'
(0.2ms) begin transaction
(0.2ms) rollback transaction
=> #<Task id: nil, name: "apples", task: nil, created_at: nil, updated_at: nil>
Then I thought, maybe my controller is wrong:
def create
Task.create(params[:task])
redirect_to tasks_path, :flash => {:success => 'We have created the task.'}
end
because it seems that my tests, using capybara, are failing - because they can't create a task.....
thoughts?
You can't save a Rails model to the database if it has a validation which fails, or a before_save callback which returns false
Related
I have 3 models that associate like so:
#user.rb
has_many :forums
has_many :posts
#forum.rb
belongs_to :user
has_many :posts
#post.rb
belongs_to :user
belongs_to :forum
I'm trying to create a single set of factories that all share the needed IDs needed to be associated with each other.
#User factory
FactoryGirl.define do
factory :user do
sequence :email do |n|
"testuser#{n}#postfactory.com"
end
password "password#1"
end
end
#Forum factory
FactoryGirl.define do
factory :forum do
user
name "Forum Name"
description "Forum Description with a minimum character count of 20"
end
end
#Post factory
FactoryGirl.define do
factory :post do
user
forum
title 'Post 1'
description 'This is a test description for Post 1'
end
end
When I run my spec test with:
user = FactoryGirl.create(:user)
forum = FactoryGirl.create(:forum)
post = FactoryGirl.create(:post)
It outputs the following in the console:
#<User id: 1, email: "testuser1#userfactory.com", created_at: "2016-10-27 20:10:36", updated_at: "2016-10-27 20:10:36">
#<Forum id: 1, name: "Forum Name", description: "Forum Description with a minimum character count o...", user_id: 2, created_at: "2016-10-27 20:10:36", updated_at: "2016-10-27 20:10:36">
#<Post id: 1, title: "Post 1", description: "This is a test description for Post 1", user_id: 3, forum_id: 2, created_at: "2016-10-27 20:10:36", updated_at: "2016-10-27 20:10:36">
As you can see, the user_id increments with each factory being created as well as forum_id. I would like these to all have the ID of 1 without having to do some manual work. What have I done incorrectly with my setup
Edit: I sort of see what I'm doing incorrectly. I only need to generate a post in my spec test and it will generate the factories needed (forum and user) to create the post. However, I do notice that I'm generating two users.
(byebug) User.count
2
(byebug) User.first
#<User id: 1, email: "testuser1#postfactory.com", created_at: "2016-10-27 20:30:33", updated_at: "2016-10-27 20:30:33">
(byebug) User.last
#<User id: 2, email: "testuser2#postfactory.com", created_at: "2016-10-27 20:30:33", updated_at: "2016-10-27 20:30:33">
Any idea why that is? I tried removing the sequence :email part and doing it standard. However, I get a validation error that the email has already been taken. For some reason, it's trying to run the user factory twice even though I call it only once in my spec test.
Every time you call FactoryGirl.create, there is a new created user, so after you run this code:
user = FactoryGirl.create(:user)
forum = FactoryGirl.create(:forum)
post = FactoryGirl.create(:post)
actually you created 3 users, as you can see post has user_id: 3.
If you want to create forum and post with user you created, you can assign that user to forum and post when they are created:
user = FactoryGirl.create(:user)
forum = FactoryGirl.create(:forum, user: user)
post = FactoryGirl.create(:post, user: user, forum: forum)
With this code, there is only one created user.
I'm having a lot of trouble understanding sessions and authentication with Rails. So basically, I tried to set a session based on user_id, following a tutorial from a book. Here is the create method in my sessions_controller.rb file:
def create
if user = User.authenticate(params[:email], params[:password])
session[:id] = user.id
redirect_to root_path, :notice => 'Logged in successfully'
else
flash.now[:alert] = "Invalid login/password combination"
render :action => 'new'
end
end
But, when I try to define a current_user method in my application_controller.rb file, it asks me to reference the session based on user_id:
def current_user
return unless session[:user_id]
#current_user = User.find_by_id(session[:user_id])
end
Here is what confuses me - user_id is an attribute of each Recipe (equivalent of articles or posts in my app) that creates a has_one relationship with a user. nowhere else in my application is user_id defined. so user_id shouldn't be an attribute of a User, right?
Just to clarify, I've listed the parameters for User objects and Recipe objects from the rails console:
2.0.0-p598 :019 > Recipe
=> Recipe(id: integer, title: string, body: text, published_at:
datetime, created_at: datetime, updated_at: datetime, user_id:
integer, photo_of_recipe_file_name: string,
photo_of_recipe_content_type: string, photo_of_recipe_file_size:
integer, photo_of_recipe_updated_at: datetime)
2.0.0-p598 :020 > User
=> User(id: integer, email: string, hashed_password: string,
created_at: datetime, updated_at: datetime, username: string)
In your create method it should be
session[:user_id] = user.id
You are setting up a key :user_id in the session hash and storing the authenticated user's id user.id in it for later use
Key name can be anything
Here is my Rspec when testing an API end point related to Users:
context "updating a user" do
let(:user) { User.create! }
it "should let me update a user without an email" do
put "/api/v1/users/#{user.id}", {:user => {:first_name => 'Willy'}}.to_json, {'CONTENT_TYPE' => 'application/json', 'HTTP_AUTHORIZATION' => "Token token=\"#{auth_token.access_token}\""}
p user.inspect
end
And the controller action that I am testing looks like this:
def update
begin
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
p #user.inspect
render json: #user, :except => [:created_at, :updated_at]
else
render json: { :errors => #user.errors }, :status => :unprocessable_entity
end
rescue ActiveRecord::RecordNotFound
head :not_found
end
end
Surprisingly, the #user.inspect in the controller shows this:
"#<User id: 2, first_name: \"Willy\", last_name: nil, email: nil, state: nil, created_at: \"2013-06-22 11:21:22\", updated_at: \"2013-06-22 11:21:22\">"
And the user.inspect in the rspec, right after the call to the controller has been done, looks like this:
"#<User id: 2, first_name: nil, last_name: nil, email: nil, state: nil, created_at: \"2013-06-22 11:21:22\", updated_at: \"2013-06-22 11:21:22\">"
Why does the Rspec not catch the updates? I mean, I have tested this manually and the database gets updated correctly.
What am I missing here?
In rspec example you define user method with let, which returns ActiveRecord object. Your controller is creating different object, that points to the same database entry. Change in db is not reflected in user object in rspec example, as there is no callback mechanism that would notify it to change.
Using #reload method on AR object in test should solve your problem, as it forces reloading data from db.
I have the following User and Post relationships:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_many :posts
end
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :user
end
I am trying to create a new Post through a User, but I get an error. I am not sure why:
1.9.3-p392 :001 > #user = User.where(:id => 1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1
=> [#<User id: 1, email: "test#test.com", encrypted_password: "$2a$10$ltpBpN0gMzOGULVtMxqgueQHat1DLkY.Ino3E1QoO2nI...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 6, current_sign_in_at: "2013-03-04 05:33:46", last_sign_in_at: "2013-03-03 22:18:17", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2013-03-02 03:41:48", updated_at: "2013-03-04 05:33:46", avatar_file_name: nil, avatar_content_type: nil, avatar_file_size: nil, avatar_updated_at: nil>]
1.9.3-p392 :002 > #user.posts.create(:content => "This is the content")
NoMethodError: undefined method `posts' for #<ActiveRecord::Relation:0x000000024ca868>
There is a difference between where and find in the ActiveRecord Relationships.
The query:
#user = User.where(:id => 1) is giving your the array hash.
So when you do something like #user.posts for the above query, it gives error of NoMethodError on ActiveRecord::Relation as there is no such post associated with this hash. So in order to convert it into the user whose id is 1, you do like this:
#user = User.where(:id => 1).first
or
#user = User.find(:id => 1)
Both will give you the user whose id is 1 (only one record) and then you can use this:
#user.posts
The above will give the associated posts of user with id 1.
Then you can do:
#user.posts.create(:content => "Post of user 1")
So what you are trying to do is actually giving you the hash (set of users) but actually you want only one user to create the relevant post.
Also, See the difference between find and where.
Your code
User.where(:id => 1)
does not give you a model instance, but a relation. Hence the NoMethodError on ActiveRecord::Relation.
Change your first line to
User.find(1)
and you're fine.
Use this
#user = User.where(:id => 1).shift
#user.posts.create(:content => "This is the content")
OR
#user = User.find(1)
#user.posts.create(:content => "This is the content")
I'm relatively new to programming, Rails, Ruby, Rspec, and the like, so thanks for your help!
My specs were very repetitive, so I wrote some spec helper methods. I can't figure out how to properly use them in my specs. Specifically, I have a users controller with create:
def create
#user = User.new(params[:user])
if #user.save
redirect_to user_path(#user)
else
render :action => :new
end
end
A bit in the spec helper that creates a valid user:
def valid_user_eilif
#test_image = Rails.root + "spec/fixtures/images/seagull.jpg"
#file = Rack::Test::UploadedFile.new(#test_image, "image/jpeg")
user = User.create!(:username => "eilif", :email => "eilif#email.org",
:image => #file, :bio => "Lots of text that I don't want to write",
:signature_quote => "Yet more text.")
user.save!
user
end
And then in my user controller spec:
before (:each) do
post :create, :user => valid_user_eilif
end
it 'should assign user to #user' do
assigns(:user).should eq(User.last)
end
When I run the spec I get the error:
Failure/Error: assigns(:user).should eq(User.last)
expected #<User id: 1, username: "eilif", email: "eilif#email.org", bio: "Lots of text that I don't want to write", signature_quote: "I feel empty.", image_file_name: "seagull.jpg", image_content_type: "image/jpeg", image_file_size: 10475, image_updated_at: "2011-05-10 23:35:55", created_at: "2011-05-10 23:35:56", updated_at: "2011-05-10 23:35:56">
got #<User id: nil, username: nil, email: nil, bio: nil, signature_quote: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, created_at: nil, updated_at: nil>
So, I assume I'm incorrectly posting to create, since nothing is created? What's the proper way to do this?
Ideally controller specs shouldn't depend on the model being able to create a row in the database. With such a simple action you can mock out the dependencies:
describe UsersController do
context "on success" do
before(:each) do
#user = mock_model(User,:save=>true)
User.stub(:new) {#user}
post :create, :user => {}
end
it "redirects" do
response.should redirect_to(user_path(#user))
end
it "assigns" do
assigns[:user].should == #user
end
end
context "on failure" do
it "renders 'new'" do
#user = mock_model(User,:save=>false)
User.stub(:new) {#user}
post :create, :user => {}
response.should render_template "users/new"
end
end
end
Notice that the specs don't pass anything in params[:user]. This helps enforce the MVC separation of concerns, whereby the model is responsible for handling the attributes, ie. validating, setting up associations, etc. You can't always keep controllers this 'skinny', but it's a good idea to try.
It looks like the problem is that #user doesn't get refreshed after the save. Try assigns(:user).reload.should eql(User.last).
But there's another slight problem, and that's probably still going to fail. You shouldn't be calling post with :user => valid_user_eilif; you want the attributes from your user record, not the actual user object itself. And you're essentially creating a new user in valid_user_eilif and then making your controller create that object again -- if you have any kind of unique constraints, you're going to get a conflict.
This is a good place to use something like factory_girl and mocks. For an example, take a look at how one of my projects handles controller specs. This example uses factory_girl, Mocha and shoulda. I'll annotate it with comments below:
describe MembersController, "POST create" do
before do
# Factory Girl - builds a record but doesn't save it
#resource = Factory.build(:member)
# Mocha expectation - overrides the default "new" behavior and makes it
# return our resource from above
Member.expects(:new).with({}).returns(#resource)
# Note how we expect it to be called with an empty hash; that's from the
# `:member` parameter to `post` below.
end
context "success" do
before do
post :create, :member => {}
end
# shoulda matchers - check for a flash message and a redirect
it { should set_the_flash.to(/successfully created/) }
it { should redirect_to(member_path(#resource)) }
end
context "failure" do
before do
# Mocha - To test a failing example in the controller, we override the
# default `save` behavior and make it return false, otherwise it would
# be true
#resource.expects(:save).returns(false)
post :create, :member => {}
end
# shoulda matchers - check for no flash message and re-render the form
it { should_not set_the_flash }
it { should render_template(:new) }
end
end