演练:创建Windows自动化测试

本教程旨在指导您如何结合Python和pytest-bdd框架,为Windows操作系统中的记事本(Notepad)应用编写自动化测试脚本。作为初学者进入桌面自动化领域的理想案例,记事本应用的自动化将介绍您如何利用CukeTest的录制功能,高效编写自动化测试项目。

录制生成脚本

1. 项目创建

  • 启动CukeTest,依次点击“文件”菜单下的“新建项目”。

  • 在弹出的对话框中,输入“NotepadTesting”作为项目名称,选择Python作为编程语言,并选中“Windows”作为项目模板。接着,设定项目存放路径。完成后,点击“创建”按钮,即可成功创建项目。

image.png

2. 开始录制脚本

在CukeTest主界面,点击“录制设置”按钮。在弹出的设置窗口中,输入待测试应用的路径。如果不预先输入路径,您可以在录制开始后使用工具栏中的“启动应用”功能来运行应用程序。对于Windows内置的记事本应用,您只需填写notepad即可启动并开始脚本录制。

录制设置界面

这里我们使用Windows自带的样例应用notepad,录制一个操作记事本对话框的场景:

  • 在记事本中输入文本"hello world"
  • 选择【格式】->【字体】
  • 从【字体】下拉框中选择"Arial"
  • 从【字形】下拉框中选择"粗体"
  • 从【大小】下拉框中选择"五号"
  • 同时单击【确定】按钮以关闭【字体...】对话框
  • 点击【文件】--【保存】
  • 在文件对话框中保存为项目路径中的"helloworld.txt"

录制时的界面如下:

录制界面

添加检查点

在自动化测试中,检查点(Checkpoint)是验证应用状态是否符合预期的关键部分。在CukeTest中,您可以在录制过程中快捷添加检查点。具体操作步骤如下:

  1. 快速打开检查点对话框:在录制时,通过按住Alt键并右击目标控件,您可以快速打开检查点对话框。这个功能让你能够在进行操作的同时,即时地添加验证步骤,提高测试脚本的编写效率。

  2. 添加属性检查点:对于本测试用例,我们将添加一个属性检查点来验证文本编辑器中的文本。在“属性检查点”列表中,选择“Text.text”属性(其值应为“hello world”),这就是我们要验证的目标属性。
    属性检查点

  3. 添加图像检查点,只需切换到图像检查点标签页,并勾选需要检查的图片。这种检查点用于验证应用界面上特定图像的出现,非常适合那些以视觉元素为核心的测试场景。这里验证字体设置有没有更改成功。
    检查点对话框

  4. 确认和插入检查点脚本:选择需要验证的属性后,点击确认按钮。相应的检查点脚本就会被插入到我们的录制脚本中。

接着可以继续操作被测应用,因为录制已经开始了的缘故,所有操作都会同步的生成自动化脚本,可以在编辑器中看到。当所有操作完成后,点击工具栏中的 “停止” 按钮,就录好了一个自动化脚本。录制生成的脚本内容如下:

Python
import os
from leanproAuto import RunSettings, WinAuto, Util

def recording():

    RunSettings.set({"slowMo": 1000, "reportSteps": True})

    modelWin = WinAuto.loadModel(os.path.dirname(os.path.realpath(__file__)) + "/recording.tmodel")

    #启动应用 "notepad"
    Util.launchProcess("notepad")
    
    #设置控件值为 "hello world"
    modelWin.getDocument("文本编辑器").set("hello world")
    
    #点击 "格式(O)"
    modelWin.getMenuItem("格式(O)").click(10, 9)
    
    #点击 "字体(F)..."
    modelWin.getMenuItem("字体(F)...").click(38, 7)
    
    #选择列表项 "Arial"
    modelWin.getList("字体(F):1").select("Arial")
    
    #选择列表项 "粗体"
    modelWin.getList("字形(Y):1").select("粗体")
    
    #选择列表项 "20"
    modelWin.getList("大小(S):1").select("20")
    
    #点击 "确定"
    modelWin.getButton("确定").click()

    #检查属性
    modelWin.getDocument("文本编辑器").checkProperty("Text.text", "hello world")
    
    #检查截屏图片
    modelWin.getVirtual("文本编辑器_image").checkImage()
    
    #点击 "文件(F)"
    modelWin.getMenuItem("文件(F)").click(23, 11)
    
    #点击 "保存(S)"
    modelWin.getMenuItem("保存(S)").click(74, 7)
    
    #设置控件值为 "helloworld.txt"
    modelWin.getEdit("文件名:1").set("helloworld.txt")
    
    #点击 "关闭"
    modelWin.getButton("关闭").click()


if __name__ == "__main__":
    recording()

3. 运行录制好的脚本

点击 工具栏【只显示脚本列】切换CukeTest视图为只显示脚本栏,点击【运行脚本】:
运行脚本

整合自动化测试用例与代码

上面的操作仅仅只是将自动化操作部分录制下来了,也就是只能回放操作步骤,还不能算是测试,因为测试脚本中除了测试操作以外还需要有断言部分,即对测试操作的结果进行验证。

接下来,我们将展示如何将录制的操作脚本转化为完整的自动化测试用例。

1. 创建应用模型

录制生成的代码运行需要依赖录制时使用的模型文件,所以在整合代码之前需要先创建本次测试需要的模型文件。这里可以直接使用录制生成的模型,将它拷贝到models目录下,并重命名为model1.tmodel

如果您的项目中已经包含模型文件,您可以同时打开新录制的模型文件和现有的模型文件,通过拖拽新录制的模型根节点到现有模型树中,实现模型的整合。之后,右击新添加的节点,选择“合并到兄弟节点”以完成合并。通过这种方法,新录制的控件对象就可以在现有模型中使用。

另外,您还可以在录制阶段选择“录制到现有模型”,这样新的控件对象将直接添加到现有模型中,简化了模型整合步骤。

2. 编写用例场景

打开目录中的.feature文件,用自然语言编写与刚刚的测试对应的业务场景。这是使测试变得更加易读、更加便于管理的重要的一步,这里我们将录制的所有操作设置成两个场景,分别是“编辑内容并保存”和“更改记事本字体”:
编写Feature文件

接下来可以切换剧本的编辑模式到 文本 模式,将以下内容复制进去,再切换回 可视 模式:

有关如何使用“Visual”视图编辑要素文件的更多信息,可以参考演练:编辑Feature文件

对应的【文本】视图内容为:

# language: zh-CN
功能: 自动化记事本应用
以记事本为例,讲解在自动化测试Windows桌面应用的时候,如何解决菜单下拉问题。
比如: 记事本的【格式】--【字体】,【文件】--【保存】

  场景: 编辑内容并保存
    当在记事本中输入文本"hello world"
    并且点击【文件】--【保存】
    同时在文件对话框中保存为项目路径中的"helloworld.txt"
    那么文件应该保存成功

  场景: 更改记事本字体
    当点击【格式】--【字体】
    并且从【字体】下拉框中选择"Arial"
    并且从【字形】下拉框中选择"粗体"
    并且从【大小】下拉框中选择"五号"
    同时单击【确定】按钮以关闭【字体...】对话框
    那么字体应该设置成功

3. 修改conftest.py

conftest.py是一个专门存放fixture的配置文件,pytest会自动识别和加载它。在CukeTest中,我们将被测应用的启动、关闭,测试环境的初始化等操作放在这个文件中。

打开根目录下的conftest.py,修改其中的应用启动路径app_path和模型文件路径。修改完成后,conftest.py内容如下:

Python
# conftest.py
from leanproAuto import Util
import os
import shutil

app_path = "notepad"

# 设置保存文件的测试缓存路径
projectPath = os.getcwd() + '\\testcache\\'
context = {}

# 等效于 BeforeAll Hook,在第一个测试开始前被调用
def pytest_sessionstart(session):
    context["pid"] = Util.launchProcess(app_path)
    # 清理缓存文件夹
    if os.path.exists(projectPath):
        shutil.rmtree(projectPath)
    os.mkdir(projectPath)


# 等效于 AfterAll Hook,在所有测试结束后被调用
def pytest_sessionfinish(session):
    Util.stopProcess(context["pid"])

这段代码主要完成以下几个任务:

  • 应用路径:定义了被测试Windows应用的路径。
  • 缓存路径:定义了缓存文件夹路径。
  • 模型加载:加载了之前提到的模型文件,这对于界面自动化测试非常重要。
  • 测试会话开始前:启动了被测试Windows应用并清理缓存文件夹。
  • 测试会话结束后:关闭被测应用。

通过以上配置,我们的测试环境将为执行自动化测试脚本做好充分准备。

4. 编写步骤代码

现在,打开位于step_defs目录下的test_feature1.py文件来开始编写步骤定义代码。

pytest-bdd通过使用装饰器,将特定的Python函数与Gherkin语法编写的.feature文件中的步骤文本关联起来。例如:

Python
from pytest_bdd import scenarios, given, when, then, parsers

# 使用parsers.parse来使步骤文本参数化,这里将接收一个"text"参数
@when(parsers.parse('我在记事本中输入文本"{text}"'), target_fixture="texts")
def enter_text(text):
    # 输入文本的逻辑处理
    ...

# 对于没有参数的步骤文本,可以直接使用字符串
@when('我点击【文件】--【保存】')
def click_save():
    # 点击保存的逻辑处理
    ...

这里,parsers.parse 允许我们将步骤文本中的变量例如 {text} 传递到步骤函数中作为参数。target_fixture 是一个pytest-bdd的特性,它使得步骤函数的返回值可以被之后的步骤作为输入参数使用。

接着,按照Feature文件中定义的步骤顺序,将录制得到的代码片段逐一复制到test_feature1.py文件的相应步骤定义中,并根据实际测试需求进行调整和优化。

完成这一步骤后,您的test_feature1.py文件将包含所有的步骤函数,它们将直接映射到Feature文件中定义的各个步骤。

为了进一步增强测试脚本,您可以使用Python的 assert 关键字添加验证点,以确认测试的预期结果。这是Python的内置断言机制,用于确认某个条件为真。您也可以利用checkProperty()方法更简便地验证GUI元素的属性。详细的检查点使用方法,请参考理解和使用检查点

以下是更新后的test_feature1.py示例,展示了完整的步骤定义及其实现:

Python
# test_feature1.py
from leanproAuto import WinAuto, Util
from pytest_bdd import scenarios, given, when, then, parsers
import pytest
import os

model = WinAuto.loadModel("models/model1.tmodel")
scenarios("../features")

# 定义缓存目录
projectPath = os.getcwd() + '\\testcache\\'

# target_fixture用来将text值传递给上下文
@when(parsers.parse('在记事本中输入文本{text}'), target_fixture="texts")
def enter_text(text):
    model.getDocument("文本编辑器").set(text)
    # 校验文本是否设置成功
    model.getDocument("文本编辑器").checkProperty("value", text)
    return text

@when('点击【文件】--【保存】')
def click_save():
    model.getMenuItem("文件(F)").click()
    model.getMenuItem("保存(S)").invoke()

@when(parsers.parse('在文件对话框中保存为项目路径中的{filename}'), target_fixture="filepaths")
def enter_filename(filename):
    # 保存到缓存路径
    filepath = projectPath + filename
    model.getEdit("文件名:1").set(filepath)
    model.getButton("保存(S)1").click()
    Util.delay(2000)
    return filepath

@then('文件应该保存成功')
def saved_successfully(texts, filepaths):
    Util.delay(2000)
    filepath = filepaths
    exist = os.path.exists(filepath)

    # 添加断言
    assert exist == True
    print(filepath + "文件已创建")

# 更改记事本字体
@when('点击【格式】--【字体】')
def click_font():
    model.getMenuItem("格式(O)").click()
    model.getMenuItem("字体(F)...").invoke()

@when(parsers.parse('从【字体】下拉框中选择{font}'))
def select_font(font):
    model.getComboBox("字体(F):").select(font)
    Util.delay(500)

@when(parsers.parse('从【字形】下拉框中选择{weight}'))
def select_weight(weight):
    model.getComboBox("字形(Y):").select(weight)
    Util.delay(500)


@when(parsers.parse('从【大小】下拉框中选择{size}'))
def select_size(size):
    model.getComboBox("大小(S):").select(size)
    Util.delay(500)


@when('单击【确定】按钮以关闭【字体...】对话框')
def close_dialog():
    model.getButton("确定").click()
    Util.delay(500)


@then('字体应该设置成功')
def font_set_successfully(request):
    #检查截屏图片
    model.getVirtual("文本编辑器_image").checkImage()

请确保步骤函数能够准确地映射到您的.feature文件中的对应步骤。在测试执行期间,pytest-bdd将根据步骤文本自动调用它们。

执行

点击【运行项目】,可以看到自动化执行完成后生成对应的测试报告。
image.png

results matching ""

    No results matching ""