Using a rails model in Capybara tests - sql

I set up a project where all I have to do is display a set of records from an SQLlite3 database. I have a table in that database, and I want the root page to display all the records in the database. I have this working, but now I want to set up a Capybara test to make sure that the page has the first and last record from the table on the page.
require 'rails_helper'
describe "seeing record from scotlands model " do
specify "I can see a list of all charities" do
visit "/"
expect(page).to have_content "#table.first_record"
end
end
However, the above provides no link to the model so I cannot access it. How do I get a link to the table from the test file?

Do you generally try to access real data from tests? I've always learned to keep those things separate.
I like to work with Rspec and Capybara. Here is something simple and straight forward that should accomplish what you've discussed:
require 'rails_helper'
feature "user sees all scotlands records" do
scenario "successfully" do
charity1 = Charity.create(name: name1, info: info1)
charity2 = Charity.create(name: name2, info: info2)
charity3 = Charity.create(name: name3, info: info3)
visit root_path
expect(page).to have_content(charity1.name)
expect(page).to have_content(charity1.info)
expect(page).to have_content(charity2.name)
expect(page).to have_content(charity2.info)
expect(page).to have_content(charity3.name)
expect(page).to have_content(charity3.info)
end
end
I actually usually work with FactoryGirl also. In this case it'd make things easier because you could use create_list and make as many records as you'd like with just one line of code.

Related

How can I reduce duplication on my automated tests?

I am testing functionality where the user can add a country and update a country. However adding the country and updating the country is done on the same screen in the same fields.
For example I can go on edit country page and create a new country by adding name and coordinates or I can select existing country and update the name and coordinates. The problem I am having is that there is validation rules on the fields such as name has to be unique and certain amount of characters.
I don't want to repeat tests but it looks like I have to because when we add new country the backend rules will INSERT a new record and I have to check that the name is not duplicate e.g. country doesn't already exist. When we update country then the backend rules will UPDATE an existing record and I have to check that name doesn't already exist.
Pseudo code Test case 1:
//Go on edit country page to add new country
//enter a name which already exists
//click save
//assert you get error message that country already exists
Pseudo code Test case 2:
//Select existing country this will open edit country page
//update the name to another country which already exists
//click save
//assert you get error message that country already exists
How can reduce the duplication in my code, I am doing everything in the Country test spec.
Tests are still code. You'd solve it the same way you'd do with any code: write a function. Some test frameworks have special facilities like RSpec shared examples.
You can also simplify the setup by sidestepping the UI and inserting data more directly.
Here's how I'd handle it with RSpec, FactoryBot, and Capybara in Ruby and Rails.
context "when a country already exists" do
shared_example "it shows a message about the duplicate country" do
it 'shows a duplicate name error' do
expect(page).to
have_content("We already have a country called #{existing_country.name}")
end
end
# We're not testing the UI can create countries, so make one using the model.
# In this case via FactoryBot to fill in all the details we don't care about.
let!(:existing_country) { create(:country) }
context "when making a new country with the same name" do
# This is all setup for the test, it's not what you're testing, so it's context.
before do
visit "/country/new"
fill_in "name", with: existing_country.name
click_button 'Save'
end
# Use the shared example.
it_behaves_like "it shows a message about the duplicate country"
# And test specific behavior. Again, we're not using the UI, we're using models.
it 'does not save the new country' do
expect(Country.where(name: existing_country.name).count).to eq 1
end
end
context "when changing a country to have the same name" do
let!(:country) { create(:country) }
let!(:old_name) { country.name }
# Again, we're not testing that the UI can edit. This is context.
before do
visit "/country/edit/#{country.id}"
fill_in "name", with: existing_country.name
click_button 'Save'
end
# Use the shared example.
it_behaves_like "it shows a message about the duplicate country"
# And test specific behavior. Again, using the models. Be sure to reload
# any existing objects, if that's how your system works, after changing them
# in the UI.
it 'does not save the new name' do
country.reload
expect(country.name).to eq old_name
end
end
end
Your details will change, but the basic ideas will remain.
Tests are just code. Break them up and share functionality similarly.
Separate setting up the context from the actual tests and reuse the context.
Avoid doing work via the UI unnecessarily. Testing the UI is complex and adds lots of code you're not testing.
Have test factories to quickly set up test data.

simple_form collection checkboxes add unexpected values to database

Environment: Rails 3.2.1
SimpleForm 2.0.1
I have encountered users here and elsewhere asking similar questions via Google, but I have not yet found answers!
I want to provide a series of checkboxes to define a contact's preferences.
I made a small test app using something I noticed on the simple_form demo app.
My Contact model has a string attribute named "post_pref"
In ContactsHelper I included this:
def contact_preference_options
['High Resolution','Web Resolution','Browser','Hard Copy Proof']
end
In my _form partial I include this:
<%= f.input :post_pref, :collection => contact_preference_options, :as => :check_boxes %>
I started the server and created a new Contact.
In the New and Edit views, the checkboxes show up. I checked "Browser" as a preference for my new Contact and submitted it.
The result of checking one or several preferences is a mess.
First:
when I go to edit a record, the current preferences aren't checked.
Second:
In the Show and Index views I see this:
--- - Browser - ''
In the console, I see this:
---\n- Browser\n- ''\n
What I want to see is this:
Browser
My questions are:
Where are the dashes coming from?
Where are the new lines coming from?
Why is there an empty string?
Why aren't the previously selected checkboxes checked when I edit a Contact?
Note: when I did the above with radio buttons or select options it works fine. But I want the contact to potentially have several preferences so I need checkboxes.
Interestingly, I tried the same thing with formtastic instead of simple_form and got almost identical results. What's the trick for checkboxes?
Thanks a million for any help.
For me it looked like it was saving the array, so I did some preprocessing of the parameters before saving it:
lifestyle = params[:lifestyle]
lifestyle[:languages] = lifestyle[:languages].reject(&:blank?).join(",")
if #lifestyle.update_attributes(lifestyle)
...
In this example, my checkboxes was languages under the lifestyle model. Hope this helps.

How do we test a Rails Model which does not have an equivalent table in the backend using RSpec

I have a Moderator model which basically queries web site related stat results from other models.
An e.g. of displayed stats could be the total number of users belonging to a particular area out of many areas in a city. Limiting the number of such records to a fixed number. For this, the body defined within the Moderator model makes use of an Area model.
Since the queries are not using anything from the same model, but actually from other models, there wasn't a need for me to have a table migration wrt this model.
I basically am now trying to test the defined methods in the Moderator model using Rspec.
What I am basically trying to test is that a call to the method should return success, this I am doing through:-
subject.send(:method_name)
response.should be_success
I'm getting a common error for all such defined methods saying that database_name.table_name does not exist. Well , this is true but how should is it really making a difference?, and how to get a work around for this to just test a simple use case of calling a method successfully in the above context.
Thanks.
Try something like this
describe Moderator do
it "should do something" do
moderator = Moderator.new
moderator.something.should == "do"
end
end
This assumes that you have a method something on Moderator and that it returns the string "do".
I think the RSpec Book provides great information and examples if you want to get more in-depth with your rspec.
Well,
The below line code did work for me:-
Model_name.method_name.should_not be_nil

cucumber step_definitions Q on style/approach on I should see

I'd like to get opinions from others with more experience on writing Cucumber step definitions.
My initial intention was to verify that a particular link was present in one scenario and
verify that it wasn't present in another scenario.
So I have the following two steps. In my naivety I thought I could use the '!find_link(...)'.
In the end I decided to use exceptions.
I'd like to solicit feedback on two aspects:
1. the approach I've taken on using the exceptions within the step_definition
2. is it meaningful to expect to explicitly look for links in the rendered page?
As I finished up work last night I couldn't help thinking "why didn't I simply use:
I should not see 'Sign up'?" and this morning it dawned on me that 'Sign up' could be included in a paragraph on the page somewhere.
Here are the steps, thanks in advance...
Then /^I should see link: ?"([^"]*)"$/ do |link|
find_link("#{link}")
end
Then /^I should not see link: ?"([^"]*)"$/ do |link|
# !find_link("#{link}") - commented out because it won't fail if link is present
begin
find_link("#{link}")
raise FindException
rescue Capybara::ElementNotFound
end
end
'Then' step definitions definition should always contain an assertion, e.g. should if using RSpec or assert if using Test::Unit.
So if want you want to verify a specific URL isn't present, it would be better to do:
should_not have_link(link)
However, it would probably be better to re-write the step as:
Then I should not see a sign-up option
And in the step definition, rather than checking for a URL, you could check for a link with a particular class:
should_not have_css('a.signup')

Displaying Error Message with Sinatra

I'm writing a simple app that takes standard input from the user. As for the email entry, I have it verify if it is in a standard email format and then have it list the problems like this when a new instance is going to be saved:
u = User.new
u.email = params[:email]
u.save
if u.save
redirect '/'
else
u.errors.each do |e|
puts e
end
end
I know that if it is correct it should return back to the home page. If it is wrong I want it to return to the home page as well, but I want it to return an error value (so I can have a pop-up or just something onscreen letting the user know that the format of the email was wrong). What would be the best way to do this?
You can use the 'sinatra-flash' gem to display all kinds of errors/notices etc.
u = User.new
u.email = params[:email]
u.save
if u.save
redirect '/'
else
flash[:error] = "Format of the email was wrong."
redirect '/'
end
Then you need to say where you want the flash[:error] to be displayed. Normally I put this in the layout.haml or (erb) file right above where I yield in the content.
layout.haml:
- if flash[:error]
%p
= flash[:error]
Also, make sure you include the gem and enable sessions
require 'sinatra'
require 'sinatra/flash'
enable :sessions
You could also try the 'rack-flash' gem. There is a tutorial for using it at http://ididitmyway.heroku.com/past/2011/3/15/rack_flash_/
You can save a potentially costly trip back and forth by doing it in Javascript. The way I see it, simple validation like this is a client function, handled by some code attached to an onBlur event, not something I need to verify on my side (except for sanitization, obviously).
To directly answer your question, I've used regular instance variables to store an "error array" in #errors. Form-specific errors, or errors that need to be displayed in a certain place on the page, rather than at the top, get stored in #form_errors or something similar. Then the template checks to see if there are errors and renders them accordingly.