验证码是UI自动化测试中最常见也最令人头疼的障碍。无论是图形验证码、滑块验证码还是短信验证码,传统脚本难以直接处理。本文将系统介绍多种解决方案:从OCR识别、模拟轨迹到更可靠的测试环境屏蔽、万能码、接口打桩,以及第三方打码平台。读完你将能根据项目情况选择最合适的验证码处理策略。
一、验证码的类型与挑战

核心原则:在测试环境中,尽量避免使用真实验证码算法。 最可靠的方法是让开发提供"测试模式"。
二、方案一:测试环境屏蔽验证码(最推荐)
与开发团队协商,在测试环境中:
固定一个万能码,如888888
注释掉验证码校验逻辑(仅测试环境)
通过特殊请求头或参数绕过
实现方式:
java
// 后端伪代码(测试环境)
if ("test".equals(env) && inputCode.equals("888888")) {
return success;
}
优点:100%稳定,无需额外代码。
缺点:需要开发和运维配合,可能影响验证码功能的回归测试。
三、方案二:OCR识别图形验证码
对于简单、无噪点、无干扰线的数字字母验证码,可以使用OCR(光学字符识别)库。
3.1 Java + Tesseract
依赖:
xml
<dependency>
<groupId>net.sourceforge.tess4j</groupId>
<artifactId>tess4j</artifactId>
<version>5.4.1</version>
</dependency>
需要下载Tesseract语言包(如eng.traineddata)并设置环境变量或指定路径。
代码示例:
java
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.image.BufferedImage;
public class CaptchaOCR {
public static String recognizeCaptcha(WebDriver driver, By captchaImgLocator) {
// 获取验证码图片元素
WebElement imgElement = driver.findElement(captchaImgLocator);
// 截取图片
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
// 定位图片位置和大小
Point point = imgElement.getLocation();
Dimension size = imgElement.getSize();
BufferedImage fullImg = ImageIO.read(screenshot);
BufferedImage captchaImg = fullImg.getSubimage(point.getX(), point.getY(), size.getWidth(), size.getHeight());
// 保存临时文件
File tempFile = new File("captcha.png");
ImageIO.write(captchaImg, "png", tempFile);
// OCR识别
Tesseract tesseract = new Tesseract();
tesseract.setDatapath("C:/tesseract/tessdata"); // 设置tessdata路径
tesseract.setLanguage("eng");
tesseract.setTessVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
try {
String result = tesseract.doOCR(tempFile).replaceAll("\\s+", "");
return result;
} catch (TesseractException e) {
e.printStackTrace();
return "";
} finally {
tempFile.delete();
}
}
}
3.2 Python + pytesseract
安装:pip install pytesseract pillow,并安装Tesseract-OCR引擎(Windows需下载安装包,并配置路径)。
代码:
python
import pytesseract
from PIL import Image
from selenium.webdriver.common.by import By
def recognize_captcha(driver, captcha_img_xpath):
# 定位验证码图片元素
img_elem = driver.find_element(By.XPATH, captcha_img_xpath)
# 截图
img_elem.screenshot("captcha.png")
# OCR识别
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe' # Windows路径
img = Image.open("captcha.png")
code = pytesseract.image_to_string(img, config='--psm 8 -c tessedit_char_whitelist=0123456789')
return code.strip()
提升识别率技巧:
对图片进行灰度化、二值化、去噪处理(使用OpenCV)
限制识别字符集(只识别数字和字母)
如果验证码固定长度(如4位),可以取前4个字符
局限性:复杂验证码(扭曲+干扰线+背景噪点)识别率很低,不建议依赖。
四、方案三:滑块验证码模拟
滑块验证码通常需要模拟人的拖动轨迹,包括加速、减速、停顿等特征。
核心思路:
获取滑块图片和背景缺口图片(或直接获取缺口位置)
计算需要滑动的距离(像素)
模拟鼠标滑动,轨迹符合人类行为
4.1 Python示例(使用OpenCV检测缺口位置)
python
import cv2
import numpy as np
from selenium.webdriver.common.action_chains import ActionChains
def get_track(distance):
"""模拟人类滑动轨迹,返回位移列表"""
track = []
current = 0
mid = distance * 4 / 5 # 前4/5加速,后1/5减速
t = 0.2
v = 0
while current < distance:
if current < mid:
a = 2 # 加速
else:
a = -3 # 减速
v0 = v
v = v0 + a * t
move = v0 * t + 0.5 * a * t * t
current += move
track.append(round(move))
# 修正总距离误差
total = sum(track)
if total != distance:
track.append(distance - total)
return track
def slide_verify(driver, slider_xpath, distance):
slider = driver.find_element(By.XPATH, slider_xpath)
action = ActionChains(driver)
action.click_and_hold(slider).perform()
track = get_track(distance)
for x in track:
action.move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
action.release().perform()
4.2 获取滑动距离的方法
通常需要两张图片:带缺口的背景图 + 滑块图。利用OpenCV的模板匹配识别缺口位置。
python
def get_distance(bg_image_path, slider_image_path):
bg = cv2.imread(bg_image_path, 0)
slider = cv2.imread(slider_image_path, 0)
# 模板匹配
result = cv2.matchTemplate(bg, slider, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# 缺口左上角x坐标
return max_loc[0]
注意:滑块验证码的机制不断升级,模拟轨迹可能会被反爬检测到。测试环境建议直接绕过。
五、方案四:第三方打码平台
对于复杂验证码,可以使用专业打码平台(如超级鹰、打码兔)。这些平台提供API,发送验证码图片即可返回识别结果。
5.1 超级鹰API示例(Python)
注册并获取软件ID和密钥。示例代码:
python
import requests
import base64
def chaojiying_captcha(img_path, codetype=1902):
# codetype: 1902 表示4位英文数字
url = "http://upload.chaojiying.net/Upload/Processing.php"
with open(img_path, 'rb') as f:
img_base64 = base64.b64encode(f.read()).decode('utf-8')
data = {
'user': 'your_username',
'pass': 'your_password',
'softid': 'your_softid',
'codetype': codetype,
'img_base64': img_base64
}
response = requests.post(url, data=data)
return response.json()['pic_str'] if response.json()['err_no'] == 0 else None
5.2 优缺点
优点:可识别复杂验证码,适用性强。
缺点:
每次识别有成本(虽然很低)
网络请求有延迟
需要处理平台稳定性问题
六、方案五:短信/邮箱验证码
对于动态验证码,常见方案:
测试环境屏蔽:后端固定返回一个验证码(如123456)。
接码平台:for SMS,使用临时手机号接收验证码(不稳定,不推荐自动化)。
后端接口调用:测试框架调用内部接口查询发送的验证码(需要开发配合暴露接口)。
示例:后端提供一个查询最近验证码的接口,仅测试环境可用。
java
// 测试代码中调用
String code = HttpUtil.get("http://internal-api/get-sms-code?phone=13800138000");
driver.findElement(By.id("smsCode")).sendKeys(code);
七、方案对比与选择建议

八、实战建议
对于正式UI自动化测试框架:
强烈建议:与开发团队沟通,在测试环境中添加"万能验证码"或屏蔽验证码校验。这是成本最低、最稳定的方案。
如果无法做到,可以尝试OCR+重试机制(识别失败刷新验证码)。
避免将复杂的滑块验证码自动化作为常规测试用例,维护成本过高。
一个通用的验证码处理流程:
java
public String solveCaptcha(WebDriver driver, By imgLocator, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
String code = recognizeCaptcha(driver, imgLocator);
if (code != null && code.length() >= 4) {
return code;
}
// 识别失败,刷新验证码
driver.findElement(By.id("refreshCaptcha")).click();
Thread.sleep(1000);
}
throw new RuntimeException("验证码识别失败");
}
九、总结

核心原则:UI自动化测试的目标是验证业务功能,而不是测试验证码算法本身。因此,在非生产环境中绕过验证码是最佳实践。