An exception is an error that occurs at run-time. It is an unwanted or unexpected event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.
I. Exception Handling In Java
Using Java’s exception handling system, we can, in a structured and controlled manner, handle run-time errors. Java’s support for it is both easy-to-use and flexible.
A principal advantage of exception handling is that it automates much of the error handling code that previous had to be entered “by hand” into any large program. Exception handling streamlines error handling by allowing your program to define a block of code, called an exception handler, that is executed automatically when an error occurs.
The Exception Hierarchy
In Java, all exceptions are represented by classes. All exception classes are derived from a class called Throwable. Thus, when an exception occurs in a program, an object of some type of exception class is generated. There are two direct subclasses of Throwable: Exception and Error. Exceptions of type Error are related to errors that occur in the Java Virtual Machine itself, and not in your program. These types of exceptions are beyond your control, and your program will not usually deal with them.
Errors that result from program activity are represented by subclass of Exception. In general, your program should handle exceptions of these types. An important subclass of Exception is RuntimeException, which is used to represent various common types of run-time errors.
Fundamentals of Exception Handling
Java exception handling is managed via five keywords: try, catch, throw, throws and finally. They form an interrelated subsystem in which the use of one implies the use of another.
Program statements that you want to monitor for exceptions are contained within a try block. If an exception occurs within the try block, it is thrown. Your code can catch this exception using catch and handle it in some rational manner. System generated exceptions are automatically thrown by the Java run-time system. To manually throw an exception, use the keyword throw. In some cases, an exception that is thrown out of a method must be specified as such by a throws clause. Any code that absolutely must be executed upon exiting from a try block is put in a finally block.
Using try and catch
At the core of exception handling are try and catch. These keywords work together; you can’t have a catch without a try. 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.
Here is the general form of the try/catch exception handling blocks:
try{
//block of code to monitor for errors
}
catch(ExceptionType1 obj){
//handler for ExceptionType1
}
Using Multiple Catch Blocks
Since there are various types of exceptions and more than one exception from a single block of code can be expected, 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{
//block of code to monitor for errors
}
catch(ExceptionType1 obj){
//handler for ExceptionType1
} catch(ExceptionType2 obj){
//handler for ExceptionType2
}
In the above, each catch must have a different type of exception. Catch expressions are checked in the order in which they occur in a program. Only a matching statement is executed. All other catch blocks are ignored.
Another important point about multiple catch statements relates to subclasses. A catch clause for a superclass will also match any of its subclasses. Since superclass of all exceptions is Throwable, to catch all possible exceptions catch Throwable. To catch exceptions of both a superclass type and a subclass type, put the subclass first in the catch sequence.
public static void main(String[] args) {
try {
int a = 1/0; // throws Arithmetic Exception
}
catch(ArithmeticException obj) {
System.out.println("Handled by Arithmetic Exception Class");
}
catch(Exception e) {
System.out.println("Handled by Exception Class");
}
}
The Consequences of an Uncaught Exception
Catching Java’s standard exceptions prevents abnormal program termination. When an exception is thrown, it must be caught by some piece of code. If your program does not catch an exception, then it will be caught by the JVM. The trouble is that the JVM’s default exception handler terminates execution and displays a stack trace and error message.
Exceptions enable you to handle errors gracefully, i.e it enables your program to respond to an error and then continue running, allowing program execution to continue.
Throwing an Exception
It is possible to manually throw an exception by using the throw statement - throw exceptionObj
Here exceptionObj must be an object of an exception class derived from Throwable.
//Manually throw an exception
class Demo{
public static void main(String[] args) {
try {
System.out.println("Before throw");
throw new ArithmeticException();
}
catch (ArithmeticException exc) {
System.out.println("Exception caught");
}
}
}
Here ArithmeticException was created using new in the throw statement, throw throws an object, and we have to create an object for it to throw i.e we can’t just throw a type.
A closer look at Throwable
A catch clause specifies an exception type and a parameter. The parameter receives the exception object. Since all exceptions are subclasses of Throwable, all exceptions support the methods defined by Throwable.
Of the methods defined by Throwable, two of the common are printStackTrace() and toString(). You can display the standard error message plus a record of the method calls that lead up to the exception by calling printStackTrace().You can use toString() to retrieve the standard error message. The toString() method is also called when an exception is used as an argument to println().
Using finally
Sometimes we will want to define a block of code that will execute when a try/catch block is left. To specify a block of code to execute when a try/catch block is exited, include a finally block at the end of a try/catch sequence.
The finally block will be executed whenever execution leaves a try/catch block, no matter what condition cause it. Whether the try block ends normally, or because of an exception, the last code executed is that defined by finally.
try{
//block of code to monitor for errors
}
catch(ExceptionType1 obj){
//handler for ExceptionType1
}
catch(ExceptionType2 obj){
//handler for ExceptionType2
}
//..
finally {
//finally code
}
Using throws
In some cases, if a method generates an exception that it does not handle, it must declare that exception in a throws clause.
public static void methodName() throws exception-list{
//body
}
Exception-list is a comma separated list of exceptions that the method might throw outside of itself. Exceptions that are subclasses of Error or RuntimeException don’t need to be specified in a throws list. All other types of exceptions do need to be declared. Failure to do so causes a compile-time error.
Java’s Built-in Exceptions
Inside the standard package java.lang, Java defines several exception classes. The most general of these exceptions are subclasses of the standard type RuntimeException. Since java.lang is implicitly imported into all Java programs, most exceptions derived from RuntimeException are automatically available. Furthermore, they need not be included in any method’s throws list. These are called as unchecked exceptions because the compiler does not check to see if a method handles or throws these exceptions.
The Unchecked Exceptions Defined in java.lang:
Exception | Meaning |
ArithmeticException | Arithmetic error, such as integer divide-by-zero. |
ArrayIndexOutOfBoundsException | Array index is out-of-bounds. |
ArrayStoreException | Assignment to an array element of an incompatible type. |
ClassCastException | Invalid cast. |
EnumConstantNotPresentException | An attempt is made to use an undefined enumeration value. |
IllegalArgumentException | Illegal argument is used to invoke a method. |
IllegalMonitorStateException | Illegal monitor operation, such as waiting on an unlocked thread. |
IllegalStateException | Environment or application is in incorrect state. |
IllegalThreadStateException | Requested operation not compatible with current thread state. |
IndexOutOfBoundsException | Some type of index is out-of-bounds. |
NeagtiveArraySizeException | Array created with a negative size. |
NullPointerException | Invalid use of a null reference. |
NumberFormatException | Invalid conversion of a string to a numeric format. |
SecurityException | Attempt to violate security. |
StringIndexOutOfBoundsException | Attempt to index outside the bounds of a string. |
TypeNotPresentException | Type not found. |
UnsupportedOperationException | An unsupported operation was encountered. |
Exceptions defined by java.lang that must be included in a method’s throws list if that method can generate one of these exceptions and does not handle it itself. These are called checked exceptions. In addition to the exceptions in java.lang, Java defines several other types of exceptions that relate to other packages, such as IOException.
The Checked Exceptions Defined in java.lang:
Exception | Meaning |
ClassNotFoundException | Class not found. |
CloneNotSupportedException | Attempt to clone an object that does not implement the Cloneable interface. |
IllegalAccessException | Access to a class is denied.
|
InstantiationException | Attempt to create an object of an abstract class or interface. |
InterruptedException | One thread has been interrupted by another thread. |
NoSuchFieldException | A requested field does not exist. |
NoSuchMethodException | A requested method does not exist. |
ReflectiveOperationException | Superclass of reflection-related exceptions. |
Java API makes extensive use of exception handling to report errors. Exception Handling in Java is one of the effective means to handle runtime errors so that the regular flow of the application can be preserved. Exceptions provide a more powerful, structured way to handle errors. They are the way Java programmers handle errors in their code.
II. Exception Handling In Selenium
Selenium WebDriver is an open-source tool to perform Browser Automation on real browsers. WebDriver communicates with browsers directly using client libraries and JSON wire protocol. It helps testers ensure that the website functions as intended on different browsers.
Though Selenium WebDriver is a powerful tool for automating web browser interactions, automation engineers often face many challenges while maintaining automation scripts in Selenium and one of the major challenges is dealing with exceptions that occur during test script execution. Let's take a look at some of the common Selenium exceptions and ways to handle/circumvent these exceptions.
Different types of Exceptions in Selenium WebDriver
Some of the Exceptions, we may face while working with Selenium WebDriver are
NoSuchElementException
This Exception occurs when the locators mentioned in the Selenium Program code is unable to find the web element on the page. There are few possibilities for getting this Exception, i.e. either we have provided an incorrect locator or we have provided correct locator, but the web element related to the locator is not available on the web page. The other possible reason could be timing issues.
2. TimeoutException
This exception is thrown when an action cannot be completed within the specified timeout duration. This exception happens when a command takes longer to complete than the wait time. Waits are mostly utilized in WebDriver to avoid the ElementNotVisibleException error. The possible cause to this exception is either Element is not interactable or due to network latency. Increasing the timeout duration and optimizing the network conditions could help in solving this exception.
3. ElementNotVisibleException
This Exception occurs when the locators provided in the Selenium Program code is trying to find the web element which is in hidden (not visible) on the page. Sometimes the test page may not entirely load before the following instruction in the program. If WebDriver attempts to locate an element on a webpage before it has fully loaded, the error ElementNotVisibleException is thrown. Waits instructions have been introduced to avoid this issue.
4. NoSuchFrameException
iframe is a HTML web page inside another HTML web page. In order to work with the Web Elements on any iframe, we have to first switch to the specific iframe and then locate the respective web elements inside the iframe. NoSuchFrameException occurs, when the driver in the Selenium Program code is unable to find the frame on the web page to switch. i.e. when the driver is switching to an invalid or non-existing iframe.
5. NoAlertPresentException
Alert is a type of pop-up, which pops up to provide important information to users. In order to work with Alert pop-ups, we have to first switch to Alert and then perform operations on Alert like reading the messages on the Alerts or Accepting/Rejecting the alert. NoAlertPresentException occurs, when the driver in the Selenium Program code is unable to find the Alert on the web page to switch to. i.e. when the driver is switching to an invalid or non-existing Alert pop-up.
6. NoSuchWindowException
This exception is thrown when an attempt is made to switch to a non-existent window. The Possible Cause for this exception could be either the window was closed or the handle is incorrect. Ensuring the existence of the window and the right handle name could help in avoiding this exception.
7. SessionNotFoundException
This Exception will occur when driver is trying to perform operations on the Web Application after the Browser is closed. This exception is thrown when a new session with the WebDriver server cannot be created. The possible cause could be either the WebDriver server is not accessible or there is an incompatible version.
Starting the WebDriver server and ensuring compatibility could help in circumventing this exception.
8. StaleElementReferenceException
Exception occurs mainly because of page navigations in between the execution of Selenium code. This exception occurs, when Selenium navigates to a different page, come backs to the same old page and performs operations on the old page. Technically, it occurs when the element defined in the Selenium code is not found in the cache memory and the Selenium code is trying to locate it. When we define an element on a page using Selenium, Selenium stores it in a cache memory. The element stored in cache memory generally gets deleted when the driver navigates away to another page during Selenium code execution. On coming back to the same old page and then trying to identify the cache removed element on the old page, we get StaleElementReferenceException as a result.
9. InvalidSelectorException
This Exception is a subclass of NoSuchElementException class and it occurs when an invalid selector is used to locate an element (Syntax error or unsupported selector). Verifying the selector syntax and using supported selectors help avoid this exception.
10. ElementNotSelectableException
This exception comes under InvalidElementStateException Class. The ElementNotSelectableException indicates that the web element is present in the web page but cannot be selected.
11. UnexpectedTagNameException
This exception is thrown when an unexpected tag name is encountered while instantiating the Select class. Use of incorrect element type or usage could result in the exception. Instantiating the Select Class with a <select> tag could help circumvent this exception.
12. ScriptTimeoutException
This exception is thrown when script execution exceeds the timeout duration.
Long-running JavaScript code or infinite loops could be the possible reasons for this exception. Optimizing the JavaScript code and increasing the timeout could help circumvent this exception.
13. InvalidElementStateException
This exception occurs when an element is not in a valid state for the desired operation.
The element is either disabled or read-only. Ensure the element is enabled and check for JavaScript events.
14. InvalidSessionIdException
This exception is thrown when an invalid session ID is used for WebDriver interaction, i.e., either the session has expired or the session ID is incorrect. Restart the WebDriver session or verify the session ID.
Common ways to handle Selenium WebDriver exceptions are:
1. Using Selenium Waits.
Selenium Wait is a set of commands that wait for a specified period of time before executing test scripts on the elements. Wait plays a very important role in executing test scripts.
When a page is loaded by the browser the elements which we want to interact with may load at different time intervals. Wait commands are essential for executing test scripts and help identify and resolve issues related to the time lag in doing particular action on web element.
Both implicit and explicit waits can be used to handle synchronization issues.
To be able to access and apply implicit wait in our test scripts, we have to first import:
import java.time.Duration;
//Implicit Wait syntax:
driver.manage().timeouts().implicitlyWait(Duration duration);
WebDriverWait specifies the condition and time for which the WebDriver needs to wait. To apply WebDriverWait in our script we have to import:
import java.time.Duration;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
//WebDriverWait syntax:
WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(15));
//Expected Condition:
wait.until(ExpectedConditions.elementToBeClickable (By.xpath("//input[@id='level1-card']")));
We create a reference variable “wait” for WebDriverWait class and instantiate it using WebDriver instance and maximum wait time “15” Seconds for the execution to layoff.
We use the “wait” reference variable of WebDriverWait class created in the previous step with ExpectedConditions class and an actual condition which is expected to occur. As soon as the expected condition occurs, it will move to execute the next line of code instead of forcefully waiting for the entire 15 seconds.
2. Scroll into the View
Selenium WebDriver is capable of manipulating the Document Object Model (DOM), and hence it doesn’t require scroll to perform certain actions. However, in some cases, there are certain web elements (for example, a submit button) that become visible only once the user has scrolled down. In such cases, automating the scroll operation becomes necessary.
Scroll can be achieved by either using Actions class or using JavaScriptExecutor.
//Scroll by Actions class
Actions action = new Actions(driver);
action.scrollToElement(element).click().perform();
or
action.scrollByAmount(0, element.getRect().y).perform();
A Scroll is a JavaScript method. The JavaScriptExecutor provides an interface that enables QAs to run JavaScript methods from Selenium scripts. Hence, to scroll up or down with Selenium, a JavaScriptExecutor is a must.
//Scroll using JavaScriptExecutor
JavaScriptExecutor js = (JavaScriptExecutor) driver;
Js.executeScript(“window.scrollBy(0,270)”, “”);
3. Check if the web element is enabled
In Selenium, to interact with an element on a web page, you must first locate it using a locator strategy such as ID, class name, name, XPath, or CSS selector.However, sometimes the element may not be present on the page at the time of execution, and your test may fail if you try to interact with it.In such cases, you can use the iselementpresent method to check if the element exists on the page before trying to interact with it.The iselementpresent method returns a boolean value indicating whether the element is present on the page. If the element is present, the method returns True; otherwise, it returns False.You can use this method in your test automation code to conditionally execute certain actions based on whether an element is present or not.
The code below verifies if an element with the id attribute value next is displayed, selected or enabled.
Syntax:
boolean eleSelected= driver.findElement(By.xpath("xpath")).isDisplayed();
boolean elePresent = driver.findElement(By.xpath("xpath")).isSelected();
boolean eleEnabled= driver.findElement(By.xpath("xpath")).isEnabled();
4. Handle overlapping elements
In some scenarios, the web element to be interacted with is blocked or overlapped by some other elements on the web page. The element such as modal dialog or pop-ups which are blocking the desired web element should be moved or dismissed from the view. Dismissing such overlapping elements is necessary to interact with the desired web element.
It is also possible that the element to be interacted with is present in the modal dialog box or alert box. In such cases, switch to the desired web element and then perform the operation.
5. Handle switching to correct frame
If the element to be interacted with is inside a frame, you need to switch to that frame using switch method as follows:
try {
driver.switchTo().frame("frame_1");
} catch (NoSuchFrameException e) {
System.out.println("Could not find the desired frame")
}
Use the appropriate method to switch to the desired frame and the web element. Also ensure that before switching to the frame, it is completely loaded.
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 codelogger.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
}
In summary
Exception handling is a critical component of any Java application or Selenium script. By handling exceptions intelligently, we can create resilient and optimum programming. It is also excellent practice to handle exceptions in a script, which will provide you with a more detailed report when a program fails for whatever reason. An exception shouldn’t be ignored as they break program execution. Exception Handling is one of the powerful mechanisms to handle the runtime errors so that the normal flow of the application can be maintained.