Search code examples
testingautomated-teststestcase

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.


Solution

  • 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.