Skip to content

基于 AI 识别方法的自动化测试


目录

  • 课程目标
  • 应用场景
  • AI 文字识别与自动化测试

课程目标

  1. 掌握自动化过程中验证码的获取方式。

  2. L1.用例录制与编写

  3. L2.高级定位与 PO 设计模式
  4. L3.webview 与微信小程序测试
  5. L4.Appium 源码分析与定制
  6. L5.分布式测试与多设备管理【当前阶段】

思考

自动化登录时验证码如何获取?


AI 识别简介

  • AI(Artificial Intelligence),指的是人工智能
  • 提取处理图像
    • 特征检测
    • 图像分割
    • 图像恢复
    • 图像压缩

常见 AI 识别深度学习框架

  • OpenCV
  • 跨平台计算机视觉和机器学习软件库。
  • 实现了图像处理和计算机视觉方面的很多通用算法。
  • PyTorch
  • Facebook 开发的一个开源深度学习框架。
  • 开源的 Python 机器学习库。
  • TensorFlow
  • Google Brain 团队开发的一个开源深度学习框架。
  • 基于数据流编程的符号数学系统。
  • 用于各类机器学习算法的编程实现。

演练环境

  • 霍格沃兹测试开发学社演练环境:
    • https://vip.ceshiren.com/#/ui_study/code

实现思路

uml diagram


未使用 AI 处理(Python)

  • Python 环境准备: pip install opencv-python
  • pip install "opencv-python-headless<4.3"
# 不加 AI 处理的场景,用例失败
    def test_code(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 == "验证成功"

使用 OpenCv + 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(2)
        # 实例化easyocr
        ocr = easyocr.Reader(['ch_sim', 'en'])
        # 识别图片内容
        code = ocr.readtext("code.png")[0][1]
        print(f"识别到的验证码为:{code}")
        # opencv处理图片
        cls.opencv_image()
        # 实例化easyocr
        ocr = easyocr.Reader(['ch_sim', 'en'])
        # 识别图片内容
        code = ocr.readtext("result.png")[0][1].replace(" ", "")
        print(f"识别到的验证码为:{code}")
        return code

    @classmethod
    def opencv_image(cls):
        # 读取图片
        image = cv2.imread("code.png")
        # 边缘保留滤波  去噪
        image1 = cv2.pyrMeanShiftFiltering(image, sp=8, sr=60)
        # 灰度图像,颜色处理
        image2 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
        # 颜色取反
        cv2.bitwise_not(image2, image2)
        # 保存图片
        cv2.imwrite('result.png', image2)
# 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 就可以很轻松地识别出来。
  • 对于复杂验证码(倾斜,背景,重叠等),需要先使用 OpenCV 对图像处理,再用 easyocr 识别
    @Test
    public void registerByOcr() 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);
    }

使用 OpenCv + 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();
        //直接识别图片内容
        System.out.println("直接识别图片内容");
        System.out.println(e.discern(input));
        // opencv处理图片
        opencvImg();
        String code = e.discern(new File("result.png"));
        System.out.println("opencv处理图片");
        System.out.println(code);
        return code;
    }

    public static void opencvImg(){
        System.load("D:\\ceba\\javaProject\\src\\main\\resources\\opencv_java340-x64.dll");
        Mat image = Imgcodecs.imread("code.png");
        // 边缘保留滤波  去噪
        Mat image1 = new Mat();
        Imgproc.pyrMeanShiftFiltering(image, image1, 8, 60, 150);
        // 灰度图像,颜色处理
        Mat image2 = new Mat();
        Imgproc.cvtColor(image1, image2, Imgproc.COLOR_BGR2GRAY);
        // 保存图片
        Imgcodecs.imwrite("result.png", image2);
    }
}

// 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 registerByOcr() 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==4.7.0.72
opencv-python-headless==3.4.18.65
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>AiCode</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>

        <!-- opencv依赖 -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.6.0-1.5.8</version>
        </dependency>
    </dependencies>

</project>