In the world of technology, exceptions are said to be as old as programming itself. Occurrence of an exception during the execution of code can result in unexpected behaviour unless the exception is properly handled in the code. Exceptions are not bad, but are a fundamental part of modern programming. Rather than having a fear of exceptions, it is important to know how to tackle which exception at what moment of time. Moving further, we would discuss how to handle exceptions elegantly and use exception handling to write clean code which is more maintainable.
What is an Exception?
Exception is a programmer’s commonly used term regardless of any specific programming language. ‘Exception’ as the name suggests are the events due to which the program ends abruptly without displaying an expected output.
Various programming languages feature multiple ways to handle exceptions. Exception handling is a commonly known process of handling an exception. Exception handling is a mandatory process as exceptions are meant to break the normal flow of execution of a program. Once the known exception is handled, the code gets prevented with a similar breakage and can continue the program execution smoothly.
There are several known conditions behind the occurrences of exception:
- Java Virtual Memory(JVM) runs out of memory
- The requested access to a file doesn’t exist in the system
- User providing invalid data
- Drop in a network in the middle of communication
Types of exceptions in Java :
- Checked Exceptions : These exceptions are checked by the compiler during the process of compilation to validate whether the exception has been handled or not. If the exception is not handled, then the system can display compilation error. Since these exceptions are validated during the compilation process, these are commonly known as compile-time exceptions. Below are few example of checked exceptions:
- SQLException : This exception probably occurs when a database query is executed based on SQL syntax.
- IOException : This exception can occur while performing invalid I/O stream operations on a file.
- ClassNotFoundException : This exception occurs when the JVM is not able to find the required Java class.
- Unchecked Exceptions : These exceptions occur during the program execution. Hence, are commonly known as Runtime Exceptions. Such exceptions are not checked during compilation and are generally ignored in the entire process of compilation. These exceptions can generally refer to logical errors. Below are few exceptions covered in category of unchecked exceptions:
- NullPointerException : This exception occurs when an object having null value is accessed.
- ArrayIndexOutofBound : This exception occurs when an array is accessed with invalid index value.
- IllegalArgumentException : This exception occurs when incorrect arguments are passed to a method.
- NumberFormatException : This exception occurs probably when a string is passed to a method that cannot be converted to a number.
- ArithmeticException : This exception occurs when an incorrect arithmetic operation is performed like dividing a number by zero.
Exception Handling Standards
Over time, Object Oriented Programming(OOP) has been enhanced to the next level in which exception handling support has become a pivotal element of modern Object Oriented Programming language. Nowadays, multiple programming languages have robust support of exception handling. Exception handling not only keeps a code clean but also enhances maintainability, extensibility and readability.
Here are some Java standards that are commonly used to handle exceptions:
Try-Catch : Combination of try-catch keywords is used to catch exceptions. The ‘try’ block is placed in the beginning while the ‘catch’ block is placed at the end to try block which helps to catch an exception and perform necessary operations if an exception is detected.
An object of exception class can be created where an exception is being encountered which can further be used to display debugging information using below predefined methods :
- printStackTrace() : This function is used to print the stack trace, exception name and other important exception information.
- getMessage() : This function helps in getting an in-depth description of an exception.
try
{
// Code
} catch (Exception e) {
// Code for Handling exception
}
Try-Catch blocks can be used in other advanced methods to handle exceptions:
- Multiple Catch Blocks : Since there are various types of exceptions and expecting more than one exception from a single block of code is not a big deal. Multiple catch blocks after try blocks can help handle different exceptions via different catch blocks. There is no limit on the number of catch blocks being used after a try block.
try
{
//Code
} catch (ExceptionType1 e1) {
//Code for Handling Exception 1
} catch (ExceptionType2 e2) {
//Code for Handling Exception 2
}
- Throw/Throws : If a programmer wants to throw an exception explicitly, the ‘throw’ keyword can be used with an exception object to be handled in runtime. In case a programmer wants to ‘throw’ an exception, then it is mandatory to handle the same exception, which can be handled by ‘throws’ keyword in the method signature so that the caller method can understand the exception that might be thrown by the method.
public static void exceptionProgram() throws Exception{
try {
// write your code here
} Catch (Exception b) {
// Throw an Exception explicitly
throw(b);
}
}
- Multiple Exceptions : Various Exceptions can be mentioned in the throws clause of method signature.
public static void exceptionProgram() throws ExceptionType1, ExceptionType2{
try {
// write your code here
} catch (ExceptionType1 e1) {
// Code to handle exception 1
} catch (ExceptionType1 e2) {
// Code to handle exception 2
}
- Finally : The ‘finally’ block is probably created after the try-catch block and always executes no matter if the exception is thrown or not.
try {
//Code
} catch (ExceptionType1 e1) {
//Catch block
} catch (ExceptionType2 e2) {
//Catch block
}
finally {
//The finally block always executes.
}
Common Exceptions in Selenium
Though there are multiple exceptions in selenium defined under WebDriverException, however, we would be looking at brief of commonly occurred exceptions with a short solution towards exception handling in selenium:
1. NoSuchElementException
This exception in selenium occurs when the WebDriver is unable to locate the desired element. NoSuchElementException is a sub-class of NotFoundException class. The common reason for this exception occurrence is the use of invalid locators.
Thinking of the other way, this exception can also occur due to latency, assuming the desired locator is on the next page and the webdriver is still stuck with either the previous page or loading the next page. Hence, handling the tests with appropriate waits can minimize the occurrence of this exception.
Further, the exception can be caught in the catch block and desired operation in the catch block can be performed to continue the execution of automated tests. Example:
try {
driver.findElement(By.id("form-save")).click();
} catch (NoSuchElementException e)
{
System.out.println(“WebDriver couldn’t locate the element”);
}
2. NoSuchWindowException
This exception is also a sub-class of NotFoundException class. The WebDriver throws NoSuchWindowException if a driver attempts to switch to invalid browser window.
The best recommended way to switch windows is to get the active window sessions first and then perform the desired operation on the windows. Example:
for (String windowHandle : driver.getWindowHandles()) {
try {
driver.switchTo().window(handle);
} catch (NoSuchWindowException e) {
System.out.println(“Exception while switching browser window”);
}
}
3. NoAlertPresentException
This exception in selenium occurs when WebDriver attempts to switch to an alert that is either not present or is invalid.
It is recommended to use explicit or fluent wait to handle browser alerts. If still the alert is not present, the exception can be caught by the catch block. Example:
try {
driver.switchTo().alert().accept();
} catch (NoSuchAlertException e)
{
System.out.println(“WebDriver couldn’t locate the Alert”);
}
4. ElementNotVisibleException
This exception in selenium occurs when WebDriver attempts to take an action on an invisible element or on the element that is not interactable. ElementNotVisibleException is defined as a subclass of ElementNotInteractableException class.
The recommended way to avoid such exception occurrences is to apply selenium waits with a decent timeout where exactly required. Example:
try {
driver.findElement(By.id("form-save")).click();
} catch (ElementNotVisibleException e)
{
System.out.println(“WebDriver couldn’t locate the element”);
}
5. ElementNotSelectableException
This exception in selenium states that the element is present on the web page but is not selectable by a WebDriver. ElementNotSelectableException is a subclass of InvalidElementStateException class.
With the catch block, the exception handling in selenium can be done and a retry of selecting the same element with same or different technique can be applied. Example:
try {
Select dropdown = new Select(driver.findElement(By.id(“swift”)));
} catch (ElementNotSelectableException e)
{
System.out.println("Element could not be selected")
}
6. NoSuchSessionException
This exception in selenium usually occurs when a test method is called after quitting the automated browser session by driver.quit() command. This exception can also occur in case if the browser crashes or the network drops.
NoSuchSessionException can be avoided by quitting the browser at the end of the test suite and making sure the browser version being used for test automation is stable.
private WebDriver driver;
@BeforeSuite
public void setUp() {
driver = new ChromeDriver();
}
@AfterSuite
public void tearDown() {
driver.quit();
}
7. StaleElementReferenceException
StaleElementReferenceException is thrown when the desired element is no longer present in DOM. This can happen when the DOM is not loaded properly or the WebDriver is stuck on a wrong page.
This exception can be caught with a catch block and a retry can be attempted with either dynamic XPath or page refresh. Example:
try {
driver.findElement(By.xpath(“//*[contains(@id,firstname’)]”)).sendKeys(“Aaron”);
} catch (StaleElementReferenceException e)
{
System.out.println("Could not interact with a desired element")
}
8. TimeoutException
This exception in selenium occurs when a WebDriver exceeds the wait time limit to perform the next step. Selenium waits are commonly used to avoid selenium exceptions like ElementNotVisibleException. Even after using appropriate waits, if the element is not interactable then TimeoutException is thrown.
To avoid this exception, manual testing has to be performed to validate the element slowness and further the waits can be handled accordingly.
9. InvalidSelectorException
This exception in selenium is thrown when invalid or incorrect selector is used. This case probably occurs while creating XPATH.
To avoid such a scenario, the test script must be reviewed and the end to end flow of script must be tested before pushing the code to the master branch. Moreover, tools like SelectorHub and ChroPath can be used to verify the locators.
10. NoSuchFrameException
This exception in selenium occurs when WebDriver attempts to switch to a frame that is either invalid or doesn’t exist on a current web page. NoSuchFrameException is a subclass of NotFoundException class.
To avoid this exception, firstly there is a need to make sure the frame’s name or id is correct, secondly, make sure the frames are not consuming time to get loaded. If frames are taking time to load on a web page, wait handling needs to be rectified. Example:
try {
driver.switchTo().frame("frame_1");
} catch (NoSuchFrameException e)
{
System.out.println("Could not find the desired frame")
}
Best practices for handling exceptions in Selenium:
Using try-catch blocks to catch and handle exceptions:
One of the fundamental best practices in exception handling is to wrap the code that may potentially throw an exception within a try-catch block. This allows you to catch the exception and handle it gracefully, preventing the script from abruptly terminating. When an exception is caught, you can perform appropriate actions such as logging the error, taking a screenshot for further analysis, or executing alternative steps to recover from the exception and continue the test execution.
Example:
try {
// Selenium code that may throw an exception
} catch (Exception e) {
// Exception handling code
// Log the error, take a screenshot, etc.
}
Employing explicit waits to handle timing-related exceptions:
Timing-related exceptions, such as NoSuchElementException or ElementNotVisibleException, often occur when the WebDriver tries to interact with an element before it is available or visible on the page. By employing explicit waits, you can instruct Selenium to wait for a certain condition to be satisfied before proceeding with the execution. This allows you to synchronize your test script with the dynamic nature of web elements and avoid timing-related exceptions.
Example:
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(“elementId”)));
Logging exceptions and providing meaningful error messages for troubleshooting:
When an exception occurs during test execution, it is crucial to log the error information to aid in troubleshooting and debugging. By logging exceptions along with relevant context information (e.g., the page URL, the actions performed before the exception), you can facilitate the identification and resolution of issues. Additionally, providing meaningful error messages helps both testers and developers understand the cause of the exception and take appropriate actions.
Example:
try {
// Selenium code that may throw an exception
} catch (Exception e) {
// Exception handling code
logger.error(“An exception occurred: ” + e.getMessage());
// Additional logging and error message customization
}
Implementing a custom exception hierarchy to handle specific scenarios:
To handle specific scenarios in your Selenium automation, you can create a custom exception hierarchy. This allows you to define and throw custom exceptions that are meaningful in the context of your application. By encapsulating specific exceptions within your custom hierarchy, you can improve code readability and maintainability.
Example:
public class CustomException extends Exception {
// Custom exception implementation
}
// Throwing a custom exception
throw new CustomException(“Custom exception message”);
Using appropriate exception types based on the nature of the error:
Selenium provides a wide range of exception types, each representing a specific error condition. When handling exceptions, it is good practice to choose the most appropriate exception type that accurately describes the nature of the error. This allows for more specific exception handling and enables better understanding and debugging of issues.
Example:
try {
// Selenium code that may throw a NoSuchElementException
} catch (NoSuchElementException e) {
// Handling NoSuchElementException
} catch (Exception e) {
// Handling other exceptions
}
Conclusion
Handling exceptions is a vital aspect of any automation script or structure of logic to accommodate every scenario. In this blog we have shared some of the exception handling commands we commonly use in selenium, be sure to understand the function of each exception before you use them in your automation script. We have also attached a helpful Whitepaper that sheds light on using the Selenium Grid Infrastructure with Docker.