I have a user model (agents) in my app who can create posts. They can do this from their dashboard. I also show all their existing posts within their dashboard. The problem comes when trying to create a new post with no content which I'm testing to make sure it renders an error. Creating a post with content works but creating one with out keeps triggering an error which says my #posts instance variable is nil. Not sure if I missed something?
Dashboard view:
.container
.column.span9
- if current_agent
= render 'home/shared/agent_post_panel'
= render 'home/shared/agent_dashboard_tabs'
agent_dashboard_tabs:
.tabs-container
#posts
.content
- if #posts.any? //This is where the error is triggered
= render partial: 'shared/post', collection: #posts
Controller for Dashboard:
class HomeController < ApplicationController
before_filter :authenticate!, only: [:dashboard]
def index
end
def dashboard
if current_agent
#post = current_agent.posts.build
#posts = current_agent.posts
end
end
end
My post controller:
class PostsController < ApplicationController
before_filter :authenticate_agent!
def create
#post = current_agent.posts.build(params[:post])
if #post.save
flash[:notice] = "Post created!"
redirect_to dashboard_path
else
flash[:error] = "Post not created"
render 'home/dashboard'
end
end
end
Tests:
feature 'Creating posts' do
let(:agent) { FactoryGirl.create(:agent) }
before do
sign_in_as!(agent)
visit dashboard_path
end
scenario "creating a post with valid content" do
fill_in 'post_content', :with => 'I love donuts'
expect { click_button "Post" }.to change(Post, :count).by(1)
end
scenario "creating a post with invalid content" do
expect { click_button "Post" }.not_to change(Post, :count)
click_button "Post"
page.should have_content("Post not created")
page.should have_content("can't be blank")
end
You are rendering home/dashboard from within posts#create when the post has errors, but you are not setting #posts variable.
You should add this before render 'home/dashboard'
#posts = current_agent.posts
Use redirect_to dashboard_path instead of render 'home/dashboard'.
and you can keep flash message on dashboard by calling keep method of flash in home#dashboard.
flash.keep
If I understand your question correctly, You are having an issue in the loading the page (HomeController#index)
As I can see prior to load your index action, you check if the user is logged in or not,
make sure your application flow goes into
if current_agent
#make sure you application flow comes here
#post = current_agent.posts.build
#posts = current_agent.posts
end
If not you might want to slightly modify this flow as (if this fits your requirment)
if current_agent
#post = current_agent.posts.build
#posts = current_agent.posts
else
#post = Post.new
#posts = []
end
and finally its always good to use try, when you are not sure about the object you get
if #posts.try(:any?)
Related
I have a Rails controller that should redirect me to a newly created post:
class PostsController < ApplicationController
def index
end
def new
#post = Post.new
end
def create
#post = Post.new(params.require(:post).permit(:date, :rationale))
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
the view for the show method is:
<%= #post.inspect %>
The following test passes:
it 'can be created from new form' do
visit new_post_path
fill_in 'post[date]', with: Date.today
fill_in 'post[rationale]', with: "Some rationale"
click_on "Save"
expect(page).to have_content("Some rationale")
end
however when I run through the browser, the redirect only goes to the index /posts
Expected Behaviour: The user should be redircted to the view show and see the newly created post
If I hard code the id into the redirect I can see the newly created post
you can find route using this, rails routes then find the routes with the posts id, either you want to use prefix/ URI pattern.
in this case, seems like you're using show.
then you can use redirect_to '/post/:id/show' for URI pattern, the id should be #post.id
I am learning how to test controllers in Rails. I have this action in my Posts Controller:
def update
#post = Post.new(post_params)
if #post.save
redirect_to posts_path
flash[:success] = "Your post has been updated"
else
render 'edit'
end
end
Pretty basic update action. I want to test it. This is the test I have right now:
require 'rails_helper'
RSpec.describe PostsController, type: :controller do
let!(:test_post) { Post.create(title: "testing", body: "testing") }
describe "PUT update" do
context "when valid" do
it "updates post" do
patch :update, id: test_post, post: {title: 'other', body: 'other'}
test_post.reload
expect(test_post.title).to eq('other')
end
end
end
end
This test does not pass. This is the error I get from RSpec:
1) PostsController PUT update when valid updates post
Failure/Error: expect(test_post.title).to eq('other')
expected: "other"
got: "testing"
(compared using ==)
I would appreciate some guidance. Thanks!
In your update action, you're creating a new Post, not updating an existing Post:
def update
#post = Post.new(post_params) <= here
if #post.save
redirect_to posts_path
flash[:success] = "Your post has been updated"
else
render 'edit'
end
end
You need to find your existing Post record, then update it. Which might look something more like:
def update
#post = Post.find_by(id: params[:id]) <= might need to be different depending on how you have structured your params
if #post.update_attributes(post_params)
redirect_to posts_path
flash[:success] = "Your post has been updated"
else
render 'edit'
end
end
I'm trying to create a very simple blog using Rails, for my own education. It's the first Rails app I've ever created other than from working through tutorials.
So far I just have a very simple model where each post has only a string for the title and a string for the content. Everything works fine and as expected in the browser, but I can't get the tests to pass.
Here's are the failing test in my Rspec code (spec/requests/post_spec.rb):
require 'spec_helper'
describe "Posts" do
.
.
.
describe "viewing a single post" do
#post = Post.create(title: "The title", content: "The content")
before { visit post_path(#post) }
it { should have_selector('title', text: #post.title) }
it { should have_selector('h1', text: #post.title) }
it { should have_selector('div.post', text: #post.content) }
end
end
This gives me the same error message for all 3:
Failure/Error: before { visit post_path(#post) }
ActionController::RoutingError:
No route matches {:action=>"show", :controller=>"posts", :id=>nil}
So it seems to me the problem is that the line #post = Post.create(...) is creating a post without an id, or else it's not saving the post to the test database correctly. How do I fix this? And am I going about this the right way in the first place, or is there a better way I could be creating the test post/testing the page?
This is only a problem in testing. When I view a single post in the browser everything looks fine. The Posts controller is: (I have edited this since posting the original question)
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => "Post successfully created!"
end
end
def index
end
def show
#post = Post.find(params[:id])
end
end
And here's the Post model in its entirety:
class Post < ActiveRecord::Base
attr_accessible :content, :title
validates :content, presence: true
validates :title, presence: true
end
config/routes:
Blog::Application.routes.draw do
resources :posts
root to: 'posts#index'
end
app/views/posts/show.html.erb:
<% provide(:title, #post.title) %>
<h1><%= #post.title %></h1>
<div class="post"><%= #post.content %></div>
Your instance variable needs to go into the before block. (the test is trying to goto /posts/:id/show and params[:id] in this case is nil as #post hasnt been created)
try:
before do
#post = Post.create(title: "The title", content: "The content")
visit post_path(#post)
end
Ok so it seems your new and create actions are empty?? Try
def new
#post =Post.new
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => " Post successfully created."
end
end
and then your view for new needs to have a form_for #post
You cant create a new post without this and only when this is successful your posts will e assigned ids
Do you need to put your 'before' stuff outside the test? This works for me:
require 'spec_helper'
describe "Posts" do
before(:each) do
#post = Post.create(title: "The title", content: "The content")
end
describe "viewing a single post" do
it "should show the post details" do
get post_path(#post)
response.status.should be(200)
# do other tests here ..
end
end
end
In my controller when an user creates a new post, he/she is redirected to the page that contains the newly created post. I'm wanting to create a test in rspec to cover this redirect but am having trouble with it. Specifically, I want to know what to write in the refirst_to argument. Here is the controller code below..
def create
#micropost = Micropost.new(params[:micropost])
respond_to do |format|
if #micropost.save
format.html {redirect_to #micropost}
else
format.html {render action: 'edit'}
end
end
end
Here is the rspec test...
before do
#params = FactoryGirl.build(:micropost)
end
it "redirects to index" do
#clearly #params.id doesn't work. its telling me instead of a redirect im getting a
#200
#response.should redirect_to(#params.id)
end
Assuming that #params will create a valid Micropost (otherwise .save will fail and you'll be rendering :edit)...
it "redirects to index on successful save" do
post :create, :micropost => #params.attributes
response.should be_redirect
response.should redirect_to(assigns[:micropost])
end
it "renders :edit on failed save" do
post :create, :micropost => {}
response.should render ... # i don't recall the exact syntax...
end
I have written this controller code in Ruby on Rails
class PostsController < ApplicationController
before_filter :authenticate_user!
def index
#posts = Post.all(:order => "created_at DESC")
respond_to do |format|
format.html
end
end
def create
#post = Post.create(:message => params[:message])
respond_to do |format|
if #post.save
format.html { redirect_to posts_path }
format.js
else
flash[:notice] = "Message failed to save."
format.html { redirect_to posts_path }
end
end
end
end
and corresponding to this I have written the following test case :-
require 'spec_helper'
describe PostsController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
describe "#create" do
it "creates a successful mesaage post" do
#post = Post.create(message: "Message")
#post.should be_an_instance_of Post
end
end
end
I am getting failures on both. Please take a look on the code and help me figure out.
I suspect you are not logged in since you are using Devise?
Maybe you need to include the devise testhelpers:
describe PostsController do
include Devise::TestHelpers
before(:each) do
#user = User.create(...)
sign_in #user
end
#assertions go here
end
As Tigraine states, it appears as though you probably are not logged in (with Devise) when the tests get executed. However, showing the failures would help in narrowing down the problem further.
On top of that, the second test isn't really an integration test and I would probably prefer something like the following to test the same condition. There are two types of test you could do:
# inside 'describe "#create"'
let(:valid_params) { {'post' => {'title' => 'Test Post'} }
it 'creates a new Post' do
expect {
post :create, valid_params
}.to change(Post, :count).by(1)
end
# and / or
it 'assigns a new Post' do
post :create, valid_params
assigns(:post).should be_a(Post)
assigns(:post).should be_persisted
end
Don't forget to add this line into your spec_helper.rb
require "devise/test_helpers"
include Devise::TestHelpers
Nevertheless, here is link for Devise wiki - How to test Controllers where you can find more info about this approach. I recommend writing the before method without (:each), what I remember it sometimes causes problems.
before do
#user = FactoryGirl.create(:user)
sign_in #user
end
Can always use:
puts response.inspect
To see how your response looks like.