How can I reduce duplication on my automated tests? - testing

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.

Related

Using a rails model in Capybara tests

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.

How to load an ObjectModel with submitted values in Prestashop?

I'm using an HelperForm to display my form which has multilang features.
I also created a Model with fields (some are multilang).
The problem is, once the user submit the form, how can I fill the data from the submit form into my new model ?
I tried this :
$instance = new MyModel();
$instance->validateController();
But for some kind of odd reasons, it doesn't save the language fields, only the "direct" fields.
I thought about using validateFields and validateFieldsLang, but they stop at the first encountered error ; I'd like to list all the possible errors at once.
How can I do this?
Are you using it from the front or back office?
Admin controllers have this
$this->copyFromPost($this->object, $this->table);
Thich basically calls the object's validation

MVC user's full name in Url, how to handle duplicates

I want to setup the following url in my MVC4 website, using the user's full name in the url:
http://www.myapp.com/profile/steve-jones
I have setup the following route in Global.asax:
routeCollection.MapRoute(
"profile", "profile/{userName}",
new { controller = "myController", action = "profile", userName = string.Empty
});
And I can take the parameter 'steve-jones' and match it to a user with matching name. My only problem though is, what if there is more than one 'Steve Jones', how can I handle this?
Does anyone know of a workaround/solution to this so that I can use a user's full name as part of the url and still be able to retrieve the correct user in the controller method?
Am I forced into including the user's id with the url (something that I do not want to appear)?
The usual way of handling this is by appending a number when creating the profiles. So if "steve-jones" is already a name in the database, then make the user's display name "steve-jones2". You basically have to insist that all profile urls are unique, which includes updating any existing database and account creation code.
Alternatively (and/or additionally), if two same names are found then have the script reroute to a disambiguation page where the user is presented with links and snippet of profile info of the many existing Steve Joneseses so they can go to the full correct profile.
Another way of handling it is by giving all user profiles an additional numeric code on the end. At my university all logins are based on name, so they give everyone pseudo-random 3-digit extensions so that they are safe as long as they don't get 1000 people with the exact same names :)
Some people might be happier being steve-jones-342 if there is no steve-jones or steve-jones1, if you're concerned.

Rails Select Box form helper for multiple booleans

Is there a way to use a select box in a rails form for multiple booleans? Let's say I have three weather conditions: Clear, Cloudy, Rainy that are each boolean. Can I put them in one select box titled "Weather", and when one of them is picked that one becomes 'true'?
To me, I see this as two different actions.
1) The user making a selection from a selection_box helper on the form. That variable gets set to the resource :current_weather and stored in the database.
2) After submit button is clicked then is more logic processed in the controller or through a class method. Let's say it was in the 'update' portion of CRUD in a weather tracker.
def update
#tracker = Tracker.find(params[:id])
if #tracker.current_weather == "Clear"
#do this
end
end
Maybe this will give you some ideas. Good luck!

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