How to test a polymorphic association on a controller with Minitest?

786 Views Asked by At

I am having trouble making a test pass for my posts controller. What is the proper way to test my create action inside the Posts controller with a polymorphic association?

I'm using Rails 4

Here's my code

Models:

class Topic < ActiveRecord::Base
  belongs_to :user
  belongs_to :category
  has_many :posts, as: :postable
  has_many :posts, dependent: :destroy
  validates :name, presence: true, length: {maximum: 50}
  validates :description, presence: true, length: {maximum: 80}
  validates :user_id, presence: true
  is_impressionable
end

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :topic
  belongs_to :category
  belongs_to :postable, polymorphic: true
  has_many :posts, as: :postable
  validates :comment, presence: true
  validates :user_id, presence: true
  validates :topic_id, presence: true
  validates :category_id, presence: true
  default_scope { order(created_at: :asc) }
end

Post controller

class PostsController < ApplicationController
  before_action :auth_user
  before_action :set_post, only: [:destroy]
  before_action :correct_user, only: [:destroy]
  before_action :find_postable, only: [:create, :new]

  def new
    @post = @postable.posts.build
  end

  def create
    @post = @postable.posts.build(post_params)
    set_topic_id
    set_category_id
    @post.user_id = current_user.id
    if @post.save
      redirect_to topic_path(@post.topic.id)
    else
      redirect_to request.referer, notice: "Post unsuccessful!"
    end
  end

  def destroy
    @post.destroy
    flash[:success] = 'Post deleted'
    redirect_to request.referer || root_url
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  def correct_user
    @post = current_user.posts.find_by(id: params[:id])
    redirect_to root_url if @post.nil?
  end

  def find_topic
    @topic = Topic.find(params[:topic_id])
  end

  def find_postable
    @postable = Post.find_by_id(params[:post_id]) if params[:post_id]
    @postable = Topic.find_by_id(params[:topic_id]) if       
     params[:topic_id]
  end

  def post_params
    params.require(:post).permit(:comment)
  end
end

Post controller test:

require 'test_helper'

class PostsControllerTest < ActionController::TestCase

  def setup
    @topic = topics(:topicone)
    @post = posts(:postone)
    @posttwo = posts(:posttwo)
    @category = categories(:categoryone)
    @user = users(:user1)
  end

  test 'should create post when logged in' do
    sign_in @user
    assert_difference 'Post.count', 1 do
      post :create, post: { user_id: @user.id, category_id:    
      @category.id, topic_id: @topic.id, comment: "First reply!",  
      postable_id: @post.id, postable_type: "Post" }
    end
  end
end

When I run the test above I get this error:

ERROR["test_should_create_post_when_logged_in", PostsControllerTest,    
  2016-12-04 14:23:25 -0500]
  test_should_create_post_when_logged_in#PostsControllerTest   
  (1480879405.93s)
NoMethodError: NoMethodError: undefined method `posts' for nil:NilClass
        app/controllers/posts_controller.rb:13:in `create'
        test/controllers/posts_controller_test.rb:28:in `block (2   
levels) in <class:PostsControllerTest>'
        test/controllers/posts_controller_test.rb:25:in `block in   
<class:PostsControllerTest>'
    app/controllers/posts_controller.rb:13:in `create'
    test/controllers/posts_controller_test.rb:28:in `block (2 levels) 
in <class:PostsControllerTest>'
    test/controllers/posts_controller_test.rb:25:in `block in 
<class:PostsControllerTest>'

From my understanding, I believe it's telling me that it can't find whether the create action is posting to a post or to a topic. The site works great in development and production. The problem is in this test. How can I rewrite this test and make it so it recognizes to whom it's posting to?

1

There are 1 best solutions below

0
On

I found a solution for this shortly after.

In this case, to successfully test if a post is created on a topic, make sure to provide the topic_id.

test 'should create post on a topic when logged in' do
  sign_in @user
  assert_difference 'Post.count', 1 do
    post :create, topic_id: @topic, post: {
                user_id: @user.id,
                category_id: @category.id,
                comment: 'First post on a topic!' }
  end
end

Now to test if a post is created on another post, make sure to provide the post_id.

test 'should create post on another post when logged in' do
  sign_in @user
  assert_difference 'Post.count', 1 do
    post :create, post_id: @post, post: {
                user_id: @user.id,
                category_id: @category.id,
                comment: 'First post on a topic!' }
  end
end