Rspec test on Simple Form Associations - ruby-on-rails

I am trying to run a request spec on a form (built with Simple Form). The form includes some select boxes that are generated using the association method and therefore database values for the model.
When running save_and_open_page it doesn't look like the select the values in the drop downs.
I have looked at Mocking and Stubbing but this is new to me and I'm still a little confused on the concept beyond basic usage.
Is there any way to generate the collection for the select box so Capybara can pick it up?
I'm using Rails 3.1, Simple Form, Capybara and FactoryGirl.
My code is...
challenge_spec
describe "New Challenges" do
before(:all) do
%w["Under 13", "13 - 16"].each do |item|
FactoryGirl.create(:age, :name => item)
end
end
it "should redirect to resources after submission" do
login_valid_user
visit new_challenge_path
#challenge = Factory.build(:challenge)
fill_in "challenge_name", :with => #challenge.name
fill_in "challenge_description", :with => #challenge.description
fill_in "challenge_description", :with => #challenge.description
select "30 mins", :from => "challenge_timescale"
save_and_open_page
select 1, :from => "challenge_age_id"
select #challenge.category, :from => "challenge_category_id"
click_button "save_button"
end
end
Controller
def new
#challenge = Challenge.new
respond_to do |format|
format.html # new.html.haml
format.json { render json: #challenge }
end
end
Form item
<%= f.association :age, :prompt => "Please select..." %>
Models
Challenge
class Challenge < ActiveRecord::Base
belongs_to :age
end
Age
class Age < ActiveRecord::Base
has_many :challenges
end

I strongly recommend creating fixtures for your tests.
This way you can manually create and manipulate the records needed for tests. It's not as efficient or elegant as using mocks, stubs and doubles but it reinforces understanding of the application and tests.

Related

Capybara string-array type fill_in fails with factory-created data

I have a people table where a person has an array of emails.
In my schema.rb it looks like this:
create_table "people", force: true do |t|
t.string "email", array: true
My person-model validates presence of email: validates :email, presence: true
My people-controller creates person like this:
def create
#person = Person.new(person_params)
#person.email << person_params["email"] #Rails 4 strong parameters
respond_to do |format|
if #person.save!
format.html { redirect_to people_url, notice: t('app.people.successful_save') }
format.json { render :index, status: :created }
else
format.html { render :index}
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
My form.html.haml asking email input:
= f.label :person, t('app.people.email')
= f.text_field :email
Factory-piece that creates email (amongst other things):
FactoryGirl.define do
factory :person do
# other stuff here...
email ["mail1#example.com"]
# ...and other stuff here
end
end
And here is my feature-spec that fails:
it 'is able to create a new person' do
person = build(:person)
visit 'people'
click_button 'New Person'
within ("#new_person_form")do
# other passing stuff here...
fill_in 'person_email', :with => person.email # <----FAILURE HERE
# ...and other not passing stuff here
end
click_button 'Save'
expect(page).to have_content 'Person saved'
end
Error message itself:
Failure/Error: fill_in 'person_email', :with => person.email
ArgumentError:
Value cannot be an Array when 'multiple' attribute is not present. Not a Array
If I googled this message, I found this:
https://github.com/jnicklas/capybara/blob/master/lib/capybara/selenium/node.rb#L30
Unfortunately I don't understand it very well. I also checked capybara cheat sheet for possible mistakes I might have been made but no good.
I get the spec to pass if I replace person.email with sth arbitrary like this:
fill_in 'person_email', :with => "sth.sth#mail.com"
I have tried different kind of values for email in factory including with and without the array brackets- same error message appears.
I get different message when I create a person object instead of building it and using plain string in my email-field inside factory instead of an array- then my model fails email presence validation. But I guess it's logical because regarding to schema, model assumes getting array not string.
I'm not very experienced in RSpec yet so maybe it's a simple mistake.. Anyway help needed, thanks!
UPDATE1:
Person class definition:
class Person < ActiveRecord::Base
validates :email, presence: true
end
fill_in 'person_email', :with => person.email
That row is actually equivalent to the following one:
find(:fillable_field, 'person_email').set(person.email)
person.email returns an instance of Array but person_email field doesn't have attribute multiple. Such usage doesn't make any sense so Capybara raises error (How can you write several values to a single text field?).
Probably you want to do the following:
fill_in 'person_email', with: person.email.first

Rspec not changing count on create

I am trying to resolve an issue with my rspec test to create an object but the count doesn't seem to change whatever i try. I am sure i am missing something very basic here.
Here is my rspec:
before do
login_account_admin(user)
#group = Factory(:group, :code => "GR_111", :description => "description for GR_111")
Group.stub!(:find).and_return(#group)
end
describe "#create" do
it "should create a new group object" do
group_params = {:code => "NEW_GROUP", :description => "description for NEW_GROUP"}
expect {
post :create, :service_id => service, :cdb_group => group_params, :button => "save", :format => "js"
}.to change(Group, :count).by(1)
end
it "should not create a new group object with invalid code format" do
group_params = {:code => "invalid", :description => "description for invalid code name group"}
expect {
post :create, :service_id => service, :cdb_group => group_params, :button => "save", :format => "js"
}.to_not change(Group, :count)
end
end
"code" parameter can only contain uppercase letters A to Z, 0-9 and _
Here is the controller method definition for #create
def create
#group = Group.new(params[:cdb_group])
respond_to do |format|
if params[:button] == "cancel"
format.js { render "hide_new"}
elsif #group.save
format.js {
render 'show_new_group'
}
format.html { redirect_to(some_path(#service), :notice => 'Group was successfully created.') }
format.xml { head :ok }
end
end
end
Here is the Group model:
class Group < ActiveRecord::Base
validates_uniqueness_of :code
validates_presence_of :code, :description
validates_format_of :code, :without => /[^A-Z0-9_]/ , :message => 'can only contain uppercase letters A to Z, 0-9 and _'
end
Whenever i try to run the rspec test I get the following errors:-
1) GroupsController User As Account Admin goes to #create should create a new group object
Failure/Error: expect {
count should have been changed by 1, but was changed by 0
# ./spec/controllers/groups_controller_spec.rb:51
2) GroupsController User As Account Admin goes to #create should not create a new group object with invalid code format
Failure/Error: expect {
count should not have changed, but did change from 2 to 1
# ./spec/controllers/groups_controller_spec.rb:58
Any help in this regard would be highly appreciated?
Whenever our tests give us unexpected trouble, it's important to take a step back and re-evaluate our approach. Usually, this is an indication of some design problem, either with the code we're testing or with tests themselves.
While it sounds like using a truncation strategy has fixed this particular problem (see more on that below), i would suggest that there is more to learn from the situation.
Consider the two examples from your spec above. The only difference between them comes down to whether the code parameter is valid or not. I would argue that these examples are really testing the Group model, not the controller.
Now, if we're confident in our model test coverage, then we can take a different approach to the controller spec. From the controller's perspective, the model is a collaborator and in general, we always want to avoid indirectly testing collaborators. In this case, we can use a mock to simulate the behavior of the Group model and only test the controller behavior in isolation.
Something like this (please note the code below is incomplete and untested):
# spec/controllers/groups_controller_spec.rb
describe "#create" do
before do
# use a Test Double instead of a real model
#new_group = double(Group)
#params = { :cdb_group => 'stub_cdb_group_param', :service_id => service }
# using should_receive ensures the controller calls new correctly
Group.should_receive(:new).with(#params[:cdb_group]).and_return(#new_group)
end
context "when cancelled responding to js" do
it "renders hide_new" do
post :create, #params.merge({:button => "cancel", :format => "js"})
expect(response).to render_template('hide_new')
end
end
context "with valid params" do
before do
#new_group.should_receive(:save).and_return(true)
end
context "responding to json" # ...
context "responding to html" # ...
context "responding to xml" #...
end
context "with invalid params" do
before do
#new_group.should_receive(:save).and_return(false)
end
# ...
end
end
While the above doesn't specifically address the problem with record counts you were having, i suspect the problem may go away once you isolate your test targets correctly.
If you choose to stick with database truncation, consider using it selectively as described here.
I hope at least some of that helps :).
After fiddling with my spec_helper.rb file. It turns out that i have to change my database cleaning strategy to truncation. Here is my spec_helper file, for reference (https://gist.github.com/aliibrahim/7152042)
I changed this line in my code and disable use of transactional_fixtures
config.use_transactional_fixtures = false
and my database cleaning strategy is now:
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
This gives a clear database before the start/end of every scenario. Hope this helps anyone!
You should test...
1) Group.create(group_params).should be_true after group_params = ...
If this fails, the problem probably related to model or test environment.
2) response.status.should == 302 after post ...
If this fails, the problem probably related to session (authentication / authorization).
3) assigns(:group).should be_valid after post ...
If this fails, the problem probably related to controller.

Testing Rails controller method with minitest

Using minitest, what's the best way to test this build_plan method?
My current attempt is to try to verify that #account.plan is getting set, but I can't quite figure out how to do that. Is this what I should be trying to do, or something else?
accounts_controller.rb
class AccountsController < ApplicationController
before_filter :build_plan, :only => [:new, :create]
def build_plan
redirect_to '/app/signup' and return unless #plan = SubscriptionPlan.find_by_name(params[:plan])
#plan.discount = #discount
#account.plan = #plan
end
end
account_integration_test.rb
require 'minitest_helper'
describe "Account integration" do
before do
#account = Factory.build(:account)
end
def fill_in_info
fill_in 'First name', :with => #account.admin.first
fill_in 'Last name', :with => #account.admin.last
fill_in 'Email', :with => #account.admin.email
end
describe "register" do
it "should set plan" do
visit signup_path(:plan => "small_group")
fill_in_info
click_button 'Create Account'
#account.plan.must_be_kind_of SubscriptionPlan #this doesn't work -- #account.plan is nil
end
end
end
For acceptance tests make sure you have the correct (presumably Capybara) DSL included in 'minitest_helper' e.g.
class MiniTest::Spec
include Capybara::DSL
...
end
It's worth reading a few other StackOverflow posts to get the right helper setup.
This one Has anyone used Minitest::Spec withing a Rails functional test? was helpful to me.
Here's a Gist of my test helper for functional tests (I have yet to add the Capybara module as above) https://gist.github.com/2990759

RSpec testing a method that uses a form builder element

I was wondering if anyone had an idea on how to implement an Rspec test which can utilize the form builder from rails. Here is what is in my helper:
def render_radio_buttons(answer, label)
check_these = []
field = '<div class="radio">'
label.split(',').each do |option|
if check_these.include?(option.strip)
field += answer.radio_button :body, option.strip, checked = true
else
field += answer.radio_button :body, option.strip
end
field += label_tag option.strip
end
field += '</div>'
end
This method is pulling a label field from the database which contains multiple comma separated labels. The answer in this case is the form builder f. How would I go about testing something like this since it needs the form builder?
If you have a better rails method on how to implement this, I would love to know! Thanks for anyone who can help!
I would just test the form using Capybara.
https://github.com/jnicklas/capybara
Capybara helps you test Rails and Rack applications by simulating how a real user would interact with your app. It is agnostic about the driver running your tests and comes with Rack::Test and Selenium support built in. WebKit is supported through an external gem.
describe "the signup process", :type => :request do
before :each do
User.make(:email => 'user#example.com', :password => 'caplin')
end
it "signs me in" do
within("#session") do
fill_in 'Login', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_link 'Sign in'
end
end

RSpec test with factory_girl and will_paginate for associated objects

I am having problems with writing a test in rspec for will_paginate. The problem is that I have a model with an Owner that can have many pets. This leads to a factories.rb file that looks like this:
Factory.define :owner do |owner|
owner.personid "1111111"
owner.firstname "Nisse"
owner.lastname "Gunnarsson"
owner.street "Street"
owner.postaladress "38830"
owner.town "Town"
owner.phone "555-5555"
owner.mobile "555-5556"
owner.email "nisse#test.com"
owner.reminder true
end
Factory.define :pet do |pet|
pet.name "Hedvig"
pet.specie "Rabbot"
pet.breed "Lowen/vadur"
pet.colour "Madagaskar"
pet.association :owner
end
In my test I have
describe "Get show" do
before(:each) do
#owner = Factory(:owner)
30.times do
##owner.pets << Factory.build(:pet)
#pet = Factory.build(:pet, :owner => #owner)
##owner.pets << #pet
end
end
it "should have an element for each pet" do
get :show, :id => #owner
#owner.pets[0..2].each do |pet|
response.should have_selector("td", :content => pet.name)
end
response.should have_selector("td", :content => "Hedvig")
end
it "should paginate pets" do
get :show, :id => #owner
response.should have_selector("div.pagination")
response.should have_selector("span.disabled", :content => "Previous")
response.should have_selector("a", :href => "/pets?page=2",
:content => "2")
response.should have_selector("a", :href => "/pets?page=2",
:content => "Next")
end
end
So I create an Owner with the factory, no problem there. I can get the owners name by puts #owner.firstname
I can also create a pet, that has the correct owner (#pet.owner.firstname), but I can not figure out how to fill the owners array (#owner.pets) with pets.
If I do a #owner.pets.count it is 0.
The applications works fine, I just can't figure out how to write the test. I am really new to both rails and TDD but I want to do it right.
Let me know if I should add more information.
Cheers Carl
Well first, doing #pet = Factory.build(:pet, :owner => #owner) only builds a Pet object, but never saves it to the DB. You would want to use Factory.create(:pet, ... to get it to actually save.
The #owner.pets array is [] when you initially create the Owner object. If you simply create records in the DB with Factory.create then yes, technically #owner has pets, but the #owner object doesn't know about them because it's already in memory with a .pets array of [].
Instead, try this:
#owner.pets << Factory.create(:pet, :owner => #owner)
That will not only save it to the database, thus making any new calls to the database valid (such as now if you did Pet.count you'd get back 1) but also the #owner.pets array in memory will have a valid Pet object within it.

Resources