How to Automate/Handle Captcha in Selenium using Java
In this blog, we will discuss How to Automate/Handle Captcha in Selenium using Java. Please read the blog.
Handling CAPTCHA using Selenium
First, we have to know, what is CAPTCHA?
CAPTCHA is an acronym for “Completely Automated Public Turing test to tell Computers and Humans Apart”.
Captchas are mainly used to determine whether the user is a human or not. It generates images or some form of tests that humans can solve but bots/ computer programs cannot.
So its main purpose is to prevent bots and other automated programs from obtaining any sensitive information from the website. Hence captcha exists to kill automation! If we can automate it, then the reason for using captchas becomes worthless.
For the purpose of testing, we can always request the developers to
- Disable captcha validation
- Provide a workaround or backdoor for obtaining the captcha value
- Tweak captcha such that it accepts one specific input value everytime
- Develop a way, where a random text gets stored in the alt attribute of the captcha image which can then be obtained and passed into the captcha text box using available locators
But anyway we have to automate captcha, then follow the three methods as follows:
Method 1
In order to solve it, there is something you can do by using API of external services such as https://www.deathbycaptcha.com. You implementing their API, passing them the CAPTCHA and get in return the text. But could not be accurate all the time.
Example for implementation
import com.DeathByCaptcha.AccessDeniedException;
import com.DeathByCaptcha.Captcha;
//Important section
import com.DeathByCaptcha.Client;
import com.DeathByCaptcha.SocketClient;
//Important section
import com.DeathByCaptcha.HttpClient;
/* Put your DeathByCaptcha account username and password here. Use HttpClient for HTTP API. */
Client client = (Client)new SocketClient(username, password);
try {
double balance = client.getBalance();
/* Put your CAPTCHA file name, or file object, or arbitrary input stream,
or an array of bytes, and optional solving timeout (in seconds) here: */
Captcha captcha = client.decode(captchaFileName, timeout);
if (null != captcha) {
/* The CAPTCHA was solved; captcha.id property holds its numeric ID, and captcha.text holds its text. */
System.out.println("CAPTCHA " + captcha.id + " solved: " + captcha.text);
if (/* check if the CAPTCHA was incorrectly solved */) {
client.report(captcha);
}
}
} catch (AccessDeniedException e) {
/* Access to DBC API denied, check your credentials and/or balance */
}
Method 2
This program uses Selenium Webdriver to get a CAPTCHA picture, sends it to DeathByCaptcha service, receives a response, types it in and gets to the secured page. As an example of a captcha-protected webpage
Let’s have a tour of the code now.
1. Opening the Webpage
First, we need to initialize the WebDriver and open the target webpage. Let’s use the Firefox driver for this:
Sinppet:
FirefoxDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
driver.navigate().to("https://testing-ground.scraping.pro/captcha");
2. Getting the Captcha Image
To get the image we will take a screenshot of the whole screen and then cut the image out according to its dimensions and location. After that the image is saved into a file in PNG format for further sending to DeathByCaptcha service:
Sinppet:
byte[] arrScreen = driver.getScreenshotAs(OutputType.BYTES);
BufferedImage imageScreen = ImageIO.read(new ByteArrayInputStream(arrScreen));
WebElement cap = driver.findElementById("captcha");
Dimension capDimension = cap.getSize();
Point capLocation = cap.getLocation();
BufferedImage imgCap = imageScreen.getSubimage(capLocation.x, capLocation.y, capDimension.width, capDimension.height);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(imgCap, "png", os);
3. Requesting the DeathByCaptcha Service
Now as we have the captcha image extracted, we can send it to DeathByCaptcha for recognition. It’s done in a couple of code lines:
SocketClient client = new SocketClient("user", "password");
Captcha res = client.decode(new ByteArrayInputStream(os.toByteArray()));
Note: replace “user” and “password” with your real DeathByCaptcha account details.
4. Typing the Recognized Captcha
As soon as we get the response from DeathByCaptcha, we can type it into the page accessing the secure part:
driver.findElementByXPath("//input[@name='captcha_code']").sendKeys(res.text);
driver.findElementByXPath("//input[@name='submit']").click();
Libraries used here are as follows:
- Selenium Webdriver for working with a webpage
- java.awt.image.BufferedImage for working with an image (extracting a part of it as a separate image)
- javax.imageio for writing the captcha image to a disk
- DeathByCaptcha API for accessing the service to turn the captcha image into a text
- java.util.logging.Logger for logging the whole process
Method 3
Checkout the following list
- If you want to test the application as it is, i.e. without making any modifications to the testing environment for the purpose of automation, then separate all test scripts that involve captcha validation and package them as a single test suite. Run this test suite with human intervention. I know this is partial automation but it is not always possible to automate everything under a given set of circumstances.
- Entire test cases except captcha can be automated.
- An implicit or an explicit wait can be used and the user can be prompted to enter the displayed captcha.
- When the test script is run, each step will be executed as usual. Once the prompt command is reached, a pop-up appears on the browser. The user enters the captcha as displayed on the screen.
- Once the captcha is entered manually, the test case execution resumes, and the subsequent steps are run on the browser.
Let us look at an example to get a better understanding.
Scenario
- Open Firefox browser
- Navigate to https://www.Path.com
- Prompt the user to intervene and enter the displayed captcha
- Locate the captcha text box by id
- Send the user entered value to the located captcha text box
- Verify JUnit pane for success result in eclipse IDE
- JUnit Code for this scenario
Code
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
//Important section
import org.junit.After;
import org.junit.Before;
//Important section
import org.junit.Test;
import org.openqa.selenium.By;
//Important section
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
//Important section
import org.openqa.selenium.firefox.FirefoxDriver;
Code
public class CaptchaValidation { //Declaring variables private WebDriver driver; private String baseUrl; @Before public void setUp() throws Exception{ // Selenium version 3 beta releases require system property set up System.setProperty("webdriver.gecko.driver", "path\\geckodriver.exe"); // Create a new instance for the class FirefoxDriver // that implements WebDriver interface driver = new FirefoxDriver(); // Implicit wait for 5 seconds driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); // Assign the URL to be invoked to a String variable baseUrl = "https://www.url.domain"; } @Test public void testPageTitle() throws Exception{ // Open baseUrl in Firefox browser window driver.get(baseUrl); //prompting user to enter captcha /*JoptionPane pops up a standard dialog box and showInputDialog prompts user for some input. Once the user enters the displayed captcha and clicks ‘OK’, it will be saved to the string “captchaVal”.*/ String captchaVal = JOptionPane.showInputDialog("Please enter the captcha value:"); //Type the entered captcha to the text box driver.findElement(By.id("idCapthca")).sendKeys(captchaVal); } @After public void tearDown() throws Exception{ // Close the Firefox browser driver.close(); } }
Comments