Negative Testing with Selenium
Sometimes our automated tests need to dig a bit deeper. Sometimes, we need to validate more than just the happy path. But sometimes we need to do negative testing. This is an easy task for a human mind, but how do we tell a script to find out how something reacts when the unexpected happens? That is what we’ll be covering today in this article.
First of all, we need to know what negative testing is. According to SmartBear, a negative test ensures that the application under test can handle invalid actions gracefully. In other words, negative tests check what happens when things go wrong.
Some negative tests are easier to implement than others. Most of the time a negative test will need to validate that an error pops up if the user does something that is considered invalid. That’s easy to find with Selenium and shouldn’t be a headache. But what happens when you want to make sure something doesn’t show up? Let’s say that there’s a visual component that’s currently not in the DOM and you want to make sure it doesn’t show up. That’s where it gets tricky.
Today we’ll see a few ways to do negative testing with Selenium, including that tricky scenario. For this article, we will be using Instagram’s website. We’ll be creating some negative tests for it.
Character Limit Tests
There a 2 ways a character limit can be implemented on a website. They can either show an error when the user exceeds the character limit, or they can prevent the user from writing after they reach the limit. Since the “showing an error” approach is verified in the same way regardless of the error, we’ll focus on how to test if the site is actually preventing the user from writing after they reach the limit.
If we take a closer look at Instagram’s Username field, it does just this. The user is only able to input 30 characters.
The idea for this is that we will locate the UserName field, send more than 50 characters to it, and then we’ll capture its data to check if the field contains more than 30 characters or not. So let’s do this step by step.
- First, we need to locate our element in our Page Object. We will do this with the following code
import pytest as pytest from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait class SignUpPage: def __init__(self, driver): self.driver = driver . . . self.username_textbox = WebDriverWait(self.driver.instance, 5).until( EC.visibility_of_element_located(( By.NAME, "username"))) . . .
- Now we need to send characters to it. We’ll create a method in our Page Object to send the keys to the WebElement. Then we’ll call this method from our Test Case file.
Our Test Case file will look like this by now. Our strings file that you can see in the code, is just a file with all the strings we will use in our code. At the moment this is how it looks.
- Now that we have our element and part of our test, we need to write the method that will validate if the field has the character limit implemented or not. We will do this by getting the “value” attribute of the username textbox. Then asserting if the count is equal to what we need.
- Now we can run our test with the following command and it will work just fine
.python3 -m pytest testcases/test_instagram.py -k
Missing Required Field Tests
Most forms deal with required fields in one of 2 ways. They either show the user in real time that a field is missing once they go past it, or they show the errors once the user submits the form. Either way is validated in the same way from Selenium’s point of view. In this case, we’ll see a case of a form that shows the error after the user submits the form.
We will run this negative scenario by writing some invalid information in the “Mobile Number or Email” field, leaving the rest of the fields alone and then click the “Next” button.
- First, we need to locate our element in our Page Object. We will do this by adding this WebElement to our Page Object:
- Now we need to send characters to this field. Just like in the previous scenario, we’ll need a method in our Page Object that writes on the WebElement and a method in our Test Case. For this we’ll add this method to our Page Object:
Then we’ll add this to our Test Case
- The third step is to add the method that will validate that the error is present. In this case, the error is shown with a <span> whose class is “coreSpriteInputError gBp1f“. What we’ll do is that, since this error shows up in several fields and doesn’t have a unique specifier, we’ll go by count. We know that in this scenario we should get 3 errors. This will depend on the site you’re testing. We’ll also need a method that clicks on the “Next” button to make the errors show up. For this we’ll need to add these methods to our Page Object:
Our Test Case will now look like this:
- Now everything we need to do is run our test and watch it pass!
python3 -mpytest testcases/test_instagram.py -k
Missing UI Component Tests
A good example of this case is that when we open this Instagram site from a browser that has never opened it before (like the clean session Selenium starts for every test), we would not want to see the login screen. But how can we confirm this via the Automation?
The thing is, Selenium cannot look for something that’s not present in the DOM. So how do you check if something doesn’t exist? Well, looking for it and letting the search fail. The tricky thing about this is that when the search fails, we will get an exception and our test will fail. The fix for this is handling that exception and if we get the exception that we’re expecting, then continuing with our test.
- In this case, we will be writing directly the method that will do the validation. The idea is that we will try to find a button with the text “Log In”, and if we can’t find it, we’ll mark it as passed. If we do find it, we’ll do an assertion and mark it as failed with a message. For this, we need to add this method to our Page Object:
- Now, we just need to run our Test Case
python3 -m pytest testcases/test_instagram.py -k
And with this, we are done! If you want to check out the source code, you can find it here.