基于 OCR 识别方法的自动化测试
目录
- 课程目标
- 应用场景
- OCR 文字识别与自动化测试
课程目标
-
掌握 自动化过程中验证码的获取方式。
-
L1.用例录制与编写
- L2.高级定位与 PO 设计模式
- L3.webview 与微信小程序测试
- L4.Appium 源码分析与定制
- L5.分布式测试与多设备管理【当前阶段】
思考
自动化登录时验证码如何获取?

OCR 文字识别简介
- 光符识别(optical character recognition)。
- 对文本资料进行扫描,然后对图像文件进行分析处理,获取文字及版面信息的过程。

常见OCR识别第三方库
- EasyOCR
- 基于Tesseract的OCR识别库,用于图像识别输出文本,目前支持80多种语言。
- 模型包含文本检测、文本识别功能。
- MuggleOCR
- 一款轻量级的ocr识别库,从名字也可以看出来,专为麻瓜设计。
- 使用非常简单。
- 主要是用于识别各类验证码,一般文字提取效果就稍差了。
- ddddOCR
- 一款用于识别验证码的开源库。
- 对一些常规的数字、字母验证码识别有奇效
演练环境
- 霍格沃兹测试开发学社演练环境:
- https://vip.ceshiren.com/#/ui_study/code
实现思路
未使用 OCR 处理(Python)
- Python 环境准备:
pip install easyocr
# 不加 AI 处理的场景,无法拿到ocr数据,用例失败
def test_code_by_ocr(self):
# 获取验证码图片链接
img_url = self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) img").get_attribute("src")
# 获取验证码内容
code = OcrCode.get_by_ocr(img_url)
# 输入验证码
self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) input").send_keys(code)
# 点击确认
self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) button").click()
sleep(1)
# 断言验证码是否正确
text = self.driver.find_element(By.CSS_SELECTOR, ".el-message p").text
print(text)
assert text == "验证成功"
使用 EasyOCR 进行文字识别(Python)
# ocr_code.py
class OcrCode:
@classmethod
def get_by_ocr(cls, img_url):
# 使用requests发请求拿到图片结果
result = requests.get(img_url, verify=False)
# 保存图片到本地
with open("code.png", "wb") as f:
f.write(result.content)
sleep(1)
# 实例化easyocr
ocr = easyocr.Reader(['ch_sim', 'en'])
# 识别图片内容,并去掉空格
code = ocr.readtext("code.png")[0][1].replace(" ", "")
print(f"识别到的验证码为:{code}")
return code
# test_code.py
class TestCode:
def setup_class(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(10)
# 打开页面
self.driver.get("https://vip.ceshiren.com/#/ui_study/code")
def teardown_class(self):
self.driver.quit()
def test_code_by_ocr(self):
# 获取验证码图片链接
img_url = self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) img").get_attribute("src")
# 获取验证码内容
code = OcrCode.get_by_ocr(img_url)
# 输入验证码
self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) input").send_keys(code)
# 点击确认
self.driver.find_element(By.CSS_SELECTOR, ".code1:nth-child(1) button").click()
sleep(1)
# 断言验证码是否正确
text = self.driver.find_element(By.CSS_SELECTOR, ".el-message p").text
print(text)
assert text == "验证成功"
未使用 AI 处理(Java)
<!-- easyocr依赖 -->
<dependency>
<groupId>cn.easyproject</groupId>
<artifactId>easyocr</artifactId>
<version>3.0.4-RELEASE</version>
</dependency>
@Test
public void codeByOcr() throws IOException, InterruptedException {
// 获取验证码图片链接
String imgUrl = driver.findElement(By.cssSelector(".code1:nth-child(1) img")).getAttribute("src");
// 获取验证码内容
String code = OcrCode.getByOcr(imgUrl);
// 输入验证码
driver.findElement(By.cssSelector(".code1:nth-child(1) input")).sendKeys(code);
// 点击确认
driver.findElement(By.cssSelector(".code1:nth-child(1) button")).click();
sleep(1000);
// 断言验证码是否正确
String text = driver.findElement(By.cssSelector(".el-message p")).getText();
System.out.println(text);
assertNotEquals("验证成功", text);
}
使用 EasyOCR 进行文字识别(Java)
// OcrCode.java
public class OcrCode {
public static String getByOcr(String imgUrl) throws IOException {
// 使用restassured发请求拿到图片结果
Response response = given().get(imgUrl).then().extract().response();
// 图片保存的目录和文件名
File input = new File("code.png");
FileOutputStream fos = new FileOutputStream(input);
// 将图片保存到文件
fos.write(response.getBody().asByteArray());
fos.close();
EasyOCR e=new EasyOCR();
//直接识别图片内容
String code = e.discern(input);
System.out.println(code);
return code;
}
}
// CodeTest.java
class CodeTest {
public static ChromeDriver driver;
@BeforeAll
public static void beforeAll() {
ChromeOptions option = new ChromeOptions();
option.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(option);
// 隐式等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("https://vip.ceshiren.com/#/ui_study/code");
}
@AfterAll
public static void afterAll() {
driver.quit();
}
@Test
public void codeByOcr() throws IOException, InterruptedException {
// 获取验证码图片链接
String imgUrl = driver.findElement(By.cssSelector(".code1:nth-child(1) img")).getAttribute("src");
// 获取验证码内容
String code = OcrCode.getByOcr(imgUrl);
// 输入验证码
driver.findElement(By.cssSelector(".code1:nth-child(1) input")).sendKeys(code);
// 点击确认
driver.findElement(By.cssSelector(".code1:nth-child(1) button")).click();
sleep(1000);
// 断言验证码是否正确
String text = driver.findElement(By.cssSelector(".el-message p")).getText();
System.out.println(text);
assertNotEquals("验证成功", text);
}
}
总结
源码地址
附录:完整依赖配置(Python)
async-generator==1.10
attrs==23.1.0
certifi==2022.12.7
cffi==1.15.1
charset-normalizer==3.1.0
colorama==0.4.6
easyocr==1.6.2
exceptiongroup==1.1.1
filelock==3.12.0
h11==0.14.0
idna==3.4
imageio==2.28.1
iniconfig==2.0.0
Jinja2==3.1.2
lazy_loader==0.2
MarkupSafe==2.1.2
mpmath==1.3.0
networkx==3.1
ninja==1.11.1
numpy==1.24.3
opencv-python-headless==4.5.4.60
outcome==1.2.0
packaging==23.1
Pillow==9.5.0
pluggy==1.0.0
pyclipper==1.3.0.post4
pycparser==2.21
PySocks==1.7.1
pytest==7.3.1
python-bidi==0.4.2
PyWavelets==1.4.1
PyYAML==6.0
requests==2.28.2
scikit-image==0.20.0
scipy==1.10.1
selenium==4.9.0
shapely==2.0.1
six==1.16.0
sniffio==1.3.0
sortedcontainers==2.4.0
sympy==1.11.1
tifffile==2023.4.12
tomli==2.0.1
torch==2.0.0
torchvision==0.15.1
trio==0.22.0
trio-websocket==0.10.2
typing_extensions==4.5.0
urllib3==1.26.15
wsproto==1.2.0
附录:完整依赖配置(Java)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>javaProject</artifactId>
<groupId>com.ceshiren</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>PictureCode</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--junit5-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<!--对应添加的依赖的作用范围-->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<!--hamcrest断言-->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<!-- selenium-->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.3.0</version>
</dependency>
<!-- rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.4.0</version>
<scope>compile</scope>
</dependency>
<!-- easyocr依赖 -->
<dependency>
<groupId>cn.easyproject</groupId>
<artifactId>easyocr</artifactId>
<version>3.0.4-RELEASE</version>
</dependency>
</dependencies>
</project>