I'm running a rspec test to make sure that two models are associated between each other with has_many and belongs_to. Here is my test below.
describe "testing for has many links" do
before do
#post = Post.new(day: "Day 1", content: "Test")
#link = Link.new(post_id: #post.id, title: "google", url: "google.com")
end
it "in the post model" do
#post.links.first.url.should == "google.com"
end
end
The test is telling me that url is an undefined method. What's wrong with my test? Or did I just miss something basic.
The model file for Post
has_many :links
The model file for Link
belongs_to :post
On top of that, the link model has the attribute post_id
You need to save both models to validate this relationship, also, you can use shoulda gem.
The code looks like:
describe Link do
it { should belong_to(:post) }
end
describe Post do
it { should have_many(:links) }
end
You need to assign your link to your post otherwise, if you do #post.links, you will get a empty array ([]), which [].first returns nil. Then your try nil.url and then you get url is an undefined method for NilClass.
#post = Post.new(day: "Day 1", content: "Test")
#link = Link.new(title: "google", url: "google.com")
#post.links << #link
Related
I have two models Page Article. For every article created a page gets created with the attributes of article. As follows:
class Article
after_save :article_page_create
def article_page_create
site = Site.find_by(data_proxy_id: self.data_proxy_id)
page = Page.where(entity_id: self.id)
if page.blank?
if article_type == 'StaticPage'
Page.create(entity_id: self.id, url: "/static/#{self.url_part}", page_type: 'static_page')
else
Page.create(entity_id: self.id, url: self.url, page_type: 'article_page')
end
else
return page.update(url: self.url) unless article_type == 'StaticPage'
page.update(url: "/static/#{self.url_part}")
end
end
end
I am trying test cases for the first time. So far this is how far I got.
article_spec.rb
require 'rails_helper'
RSpec.describe Article, type: :model do
context 'validation tests' do
it 'ensures article attrs presence' do
page = Page.create(entity_id: self.id, url: "/static/#{self.url_part}", page_type: 'static_page')
expect(page).to eq(true)
end
end
end
I just wanted know is this the way to test my after_save method. Correct me if I am wrong, please.
Hmmmm, I think I can help out here.
When testing callbacks, you need to test two assumptions:
Is it being called for the correct event?
Is it doing what it's supposed to be doing?
And remember, you want to try make sure your tests cover one specific case.
Your specs should be reading:
On saving an article,
I expect the class to receive a callback method
On saving a new article,
I expect the number of Page elements to increase by one
On saving an old article,
I expect an existing page to be updated
You can continue to flesh out based on the article types etc.
E.g.
it 'triggers a callback to create a page on save' do
expect(my_class_instance).to receive(:article_page_create)
#article.save
end
context 'when creating a new page' do
it "creates a new article" do
# expect number of pages to change by 1
end
end
context 'when updating an old page' do
it 'updates the corresponding article' do
# expect attribs to be correct for corresponding page
end
end
I have a Rails 4 application with a series of associations: Posts have_many Reviews, Reviews have_one Assumption. I have set up Factories to generate Reviews with Assumptions:
FactoryGirl.define do
factory :review do
association :post, factory: :post
...fields...
factory :review_with_assumption do
after(:create) do |review|
FactoryGirl.create(:assumption, assumable: review)
end
end
end
end
The following works perfectly in the console:
post = FactoryGirl.create(:post)
rev = FactoryGirl.create(:review_with_assumption, post: post)
rev.assumption.valid?
=> true
post.reviews.first.assumption.valid?
=> true
And all the fields are exactly as they are set in my Factory definitions -- for example, each Assumption includes a 'score' of 6. However, when I use the following Cucumber steps, the Review is created but its Assumption is not:
Given(/^the post "(.*?)"'s review "(.*?)" has an assumption$/) do |arg1, arg2|
post = FactoryGirl.create(:post, name: arg1)
rev = FactoryGirl.create(:review_with_assumption, name: arg2, post: post)
end
Then(/^I should see the "(.*?)" review assumption's score$/) do |arg2|
rev = Review.find_by_name(arg2)
page.should have_content rev.assumption.score
end
So I get an error that looks like:
And I should see the "Test Rev" review assumption's score
undefined method `score' for nil:NilClass (NoMethodError)
The page content it is trying to render works perfectly in the browser if I input everything manually. Why doesn't it also work through Factory Girl and Cucumber?
You don't have a "review" record whose name is "arg1". Instead, according to your definition, the one having name as "arg1" is the post record.
I am new in Rspec testing of Ruby on Rails and I have a question concerning controller tests.
Please, suggest me how to test this line:
#dreams = Dream.public_dreams.includes(:user).where("users.type_id = ?", 5)
In other words, I want to test if correct parameters were set in the controller. It should display all the dreams of a users with type_id equal to 5.
Can someone, please, help me out?
Since you've indicated that you "want to make sure...displayed dreams [are] only of users who have type_id equal to 5", this would seem to me more like a model spec than a controller spec, and I would probably refactor the code and spec it out to look something like this (assuming you still want to keep your rigid conditions):
First, refactor query into a scope in the model:
class DreamsController < ApplicationController
def your_action
#dreams = Dream.public_dreams_for_type_five_users
end
end
class Dream < ActiveRecord::Base
def self.public_dreams
# your code here to access public dreams
end
def self.public_dreams_for_type_five_users
public_dreams.includes(:user).where("users.type_id = ?", 5)
end
end
Next, test the scope in a model spec against some entries in the database that will pass and fail your expectations (the following spec uses FactoryGirl syntax, but you can substitute it out for whatever fixture-substitute library you like):
require 'spec_helper'
describe Dream do
describe ".public_dreams_for_type_five_users" do
let(:type_five_user_public_dreams) { Dream.public_dreams_for_type_five_users }
context "for users where type_id is 5" do
let!(:type_five_dream) { create(:dream, user: create(:user, type_id: 5)) }
it "includes the user's public dreams" do
expect(type_five_user_public_dreams).to include(type_five_dream)
end
end
context "for users where type_id is not 5" do
let!(:type_six_dream) { create(:dream, user: create(:user, type_id: 6)) }
it "does not include the user's public dreams" do
expect(type_five_user_public_dreams).to_not include(type_six_dream)
end
end
end
end
If you wanted, you could then go and further generalise the class method to be something like Dream.public_dreams_for_users_of_type(id) and change the specs accordingly.
There are several answers to that:
You could test the query itself:I would put such a query in a method or scope of your Dream model.Then go and test the query in a model spec.
You could test the assignment:On the other hand you can test the correct assignment in a controller spec with assigns[:dreams]
#dreams.select { |dream| dream.user.type_id = 5 }.should eq(#dreams) ?
I have a rails controller, defined here:
https://github.com/abonec/Simple-Store/blob/master/app/controllers/carts_controller.rb
On the cart page a user can specify the quantity of line_items by posting nested attributes. The parameters look like this:
{ "cart" => {
"line_items_attributes" => {
"0" => {
"quantity" => "2",
"id" => "36" } } },
"commit" => "Update Cart",
"authenticity_token" => "UdtQ+lchSKaHHkN2E1bEX00KcdGIekGjzGKgKfH05So=",
"utf8"=>"\342\234\223" }
In my controller action these params are saved like this:
#cart.update_attributes(params[:cart])
But I don't know how to test this behavior in a test. #cart.attributes only generates model attributes not nested attributes.
How can I test this behavior? How to simulate post request with nested attributes in my functional tests?
A little late to the party, but you shouldn't be testing that behavior from the controller. Nested attributes is model behavior. The controller just passes anything to the model. In your controller example, there is no mention of any nested attributes. You want to test for the existence of the behavior created by accepts_nested_attributes_for in your model
You can test this with rSpec like this:
it "should accept nested attributes for units" do
expect {
Cart.update_attributes(:cart => {:line_items_attributes=>{'0'=>{'quantity'=>2, 'other_attr'=>"value"}})
}.to change { LineItems.count }.by(1)
end
Assuming you're using Test::Unit, and you have a cart in #cart in the setup, try something like this in your update test:
cart_attributes = #cart.attributes
line_items_attributes = #cart.line_items.map(&:attributes)
cart_attributes[:line_items] = line_items_attributes
put :update, :id => #cart.to_param, :cart => cart_attributes
Using test/unit in Rails3, first generate a integration test:
rails g integration_test cart_flows_test
in the generated file you include you test, something like:
test "if it adds line_item through the cart" do
line_items_before = LineItem.all
# don't forget to sign in some user or you can be redirected to login page
post_via_redirect '/carts', :cart => {:line_items_attributes=>{'0'=>{'quantity'=>2, 'other_attr'=>"value"}}}
assert_template 'show'
assert_equal line_items_before+1, LineItem.all
end
I hope that helped.
After you update the cart with the nested attributes, you can access the nested attributes by doing
#cart.line_items
I've got a pretty simple rails app with a form that shows all outstanding Invites, and then a form to add a new Invite (that uses #relation.invites.build).
The problem is that in listing all those existing Invites, a final one is shown with no data -- the new Invite for the form.
E.g. Invite.all includes [{id: 1, email: bill#example.com}, {id: 2, email: pat#example.com}, {id: nil, email: nil}].
I could make a simple :persisted scope to filter out the new one, but this seems like a smell and I'm wondering if a pattern or convention exists to deal with this little issue.
Edited.
My previous example did not actually work as I had expected.
calling .build on a relation actually modifies the value stored in memory as it is by reference.
Instead you can use Invite.new(relation: #relation).
class InvitesController < ApplicationController
before_action :set_relation, only: [:index]
def index
#invites = #relation.invites
#new_invite = Invite.new(relation: #relation)
end
def set_relation
#relation = Relation.joins(:invites).find(params[:relation_id])
end
end
require 'rails_helper'
RSpec.describe InvitesController, type: :controller do
describe "GET #index" do
let!(:relation) { create(:relation) }
let!(:invite) { create(:invite, relation: relation) }
before { get :index, relation_id: relation }
it "does not include the new invite in #invites" do
expect(assigns(:invites)).to eq [invite]
end
it "assigns a new invite as #new_invite" do
expect(assigns(:new_invite)).to be_a_new Invite
end
it "assigns the correct relation to the new invite" do
expect(assigns(:new_invite).relation).to eq relation
end
end
end
Another alternative is to cast the association collection to an array:
def index
#invites = #relation.invites.to_a # is not by reference
#new_invite = #relation.invites.build
end
Please try select(&:persited?). This will select only persisted records for the invites excluding the initialized one. You dont need to make a scope,when it is already there.
#relation.invites.select(&:persisted?)
Hope this will help.