lyyyuna 的小花园

动静中之动, by

RSS

Robot Framework 快速入门指南

发表于 2015-12

前言

关于本指南

《Robot Framework 快速入门指南》介绍了 Robot Framework 的一些最重要的特性。你不仅可以打开并浏览这些例子,而且你也可以把本指南当成一个 可执行的演示程序。所有这些特性在《Robot Framework 用户指南》中有详细的介绍。

Robot Framework 概览

Robot Framework 是一个通用的开源自动化测试框架,常被用作验收测试 (acceptance testing) 和验收测试驱动开发 (acceptance test-driven development, ATDD)。它有着易用的表格测试数据语法,并且采用了关键字驱动的测试方法。你可以使用 Python 或者 Java 编写的测试库莱扩展框架的测试能力,而且用户可以使用和测试用例相同的语法,来创建新的高级关键字。

Robot Framework 独立于操作系统和应用。核心框架采用 Python 编写,同样能够在 Jython (JVM) 和 IronPython (.NET) 上运行。该框架有着丰富的生态系统,包含多种多样独立开发的通用测试库和工具。

有关 Robot Framework 极其生态系统更多的信息,你可以浏览 http://robotframework.org/。在那,你可以看到更多丰富的文档,演示程序,测试库和其他的工具列表,等等。

演示程序

在本指南中的示例应用程序是一个经典的登录示例的变体:这是用 Python 编写的基于命令行的身份验证服务器。该应用程序允许用户做三件事:

应用程序本身是文件 sut/login.py 中,可以直接执行命令 python sut/login.py。如果试图用一个不存在的用户帐户,或者无效的密码登陆,都会显示如下的错误消息:

> python sut/login.py login nobody P4ssw0rd
Access Denied

当创建一个有效帐户和密码,并登录成功后:

> python sut/login.py create fred P4ssw0rd
SUCCESS

> python sut/login.py login fred P4ssw0rd
Logged In

当使用无效凭据更改密码,显示的错误消息和之前相同。新密码会进行有效性验证,如果无效,则给出如下错误消息:

> python sut/login.py change-password fred wrong NewP4ss
Changing password failed: Access Denied

> python sut/login.py change-password fred P4ssw0rd short
Changing password failed: Password must be 7-12 characters long

> python sut/login.py change-password fred P4ssw0rd NewP4ss
SUCCESS 

该应用程序使用一个简单的数据库文件来追踪用户状态。该文件位于操作系统相关的临时目录。

执行演示程序

这些说明会解释如何自己运行本指南的演示程序,如果你不感兴趣,你仍然可以查看 在线结果

安装

推荐使用 Pythonpip 工具来安装 Robot Framework。你可以直接运行:

pip install robotframework

你可以查看 Robot Framework 安装指南,了解更多的安装方法,和有关安装的更多一般信息。

这个示例是使用 reStructuredText 标记语言书写,采用框架的代码块中的测试数据。想要用这种格式执行本测试需要安装 [docutils](Robot Framework test data in code blocks) 模块:

pip install docutils

请注意,目前官方还不支持 Python3。请查阅 安装指南 了解非官方的 Python3 移植,极其最近的支持程度。

执行

安装完以后,你还需要获取演示实例。最方便的方法就是下载一个 发布版本 或者获取 最新版本 后在任意位置解压,你也可以直接克隆这个 仓库

当你安装完,并获取一切必要条件之后,你可以用 pybot 命令运行本演示:

pybot QuickStart.rst

你还可以配置各种命令行选项来执行:

pybot --log custom_log.html --name Custom_Name QuickStart.rst

运行 pybot --help 来获取可用的选项列表。

查看结果

运行演示程序将会生成以下三个结果文件。这些文件被在线链接到可用的预执行文件,但运行演示程序时会在本地创建它们。

测试用例

工作流测试

Robot Framework 测试用例是使用简单的表格语法创建。例如,下面的表有两个测试:

请注意,这些测试读起来就像是用英语书写的手动测试步骤,而并不像是自动化测试。这是因为 Robot Framework 采用关键字驱动的测试方法,使得编写测试时能够使用自然语言,来描述过程的步骤和预期结果。测试用例是由关键字和其可能的参数构成。

高级别测试

测试用例也可以使用更抽象的关键字创建,这些例子没有任何位置参数。这允许使用更自由的文字,甚至方便那些不懂技术的客户或其他项目利益相关者互相交流。在 验证测试驱动开发 或其他变体中这种方法尤其重要。

Robot Framework 并不强制要求测试用例的格式。一个常见的风格是使用 given-when-then 风格,即 行为驱动开发 (behavior-driven development, BDD)。

*** Test Cases ***
User can change password
    Given a user has a valid account
    When she changes her password
    Then she can log in with the new password
    And she cannot use the old password anymore

数据驱动测试

我们经常会碰到这种情况,测试用例相似仅仅是输入或输出数据不同。在这种情况下_数据驱动测试_允许不同的测试数据,而无需重复工作流。在 Robot Framework 中使用 [Template] 设置,可以将用例转换为数据驱动的测试,而用例中定义的数据作为模板关键字运行:

*** Test Cases ***
Invalid password
    [Template]    Creating user with invalid password should fail
    abCD5            ${PWD INVALID LENGTH}
    abCD567890123    ${PWD INVALID LENGTH}
    123DEFG          ${PWD INVALID CONTENT}
    abcd56789        ${PWD INVALID CONTENT}
    AbCdEfGh         ${PWD INVALID CONTENT}
    abCD56+          ${PWD INVALID CONTENT}

除了在单独的测试中使用 [Template] 设置,也可以在之后介绍的 启动和卸载 的设置表格中使用 Test Template 选项。在本例中,模板能够避免为了过长或过短密码,再去创建其他无效的用例。如果不使用模板,你就不得不为每一个输入输出创建一个用例,而使用模板只要一个用例。

请注意,上述例子的错误消息是使用 变量 来指定。

关键字

测试用例的关键字有两种来源。库关键字 来自导入的测试库,所谓的 用户关键字 可由和创建测试用例相同的表格语法来书写。

库关键字

所有在测试库中定义的低级关键字都是由标准编程语言实现的,通常是 Python 或者 Java。Robot Framework 有着丰富的 测试库,它们被分成_标准库_,_外部库_和_自定义库_。标准库分布于核心框架和一些通用库中,比如 OperatingSystem, Screenshot 和 BuiltIn 中。 这些库比较特别,当安装完框架后便是可用的。而如用于网络测试的 Selenium2Library,必须单独安装。如果这些库还不够用, 创建自定义库 是非常简单的。

为了使用一个测试库提供的关键字,必须先导入该库。本指南中的测试需要 OperatingSystem 库 (如 Remove File) 和自定义库 LoginLibrary 库 (如 Attempt to login with credentials)。它们都在如下的设置表格中导入:

*** Settings ***
Library           OperatingSystem
Library           lib/LoginLibrary.py

用户关键字

Robot Framework 最强大的特性是允许用户使用已有关键字来创建新的高级关键字。这种语法被称作_用户自定义关键字_,或者简称为_用户关键字_。关键字语法和创建测试用例是类似的。上述例子中所有的高级关键字都在如下的关键字表格中创建:

*** Keywords ***
Clear login database
    Remove file    ${DATABASE FILE}

Create valid user
    [Arguments]    ${username}    ${password}
    Create user    ${username}    ${password}
    Status should be    SUCCESS

Creating user with invalid password should fail
    [Arguments]    ${password}    ${error}
    Create user    example    ${password}
    Status should be    Creating user failed: ${error}

Login
    [Arguments]    ${username}    ${password}
    Attempt to login with credentials    ${username}    ${password}
    Status should be    Logged In

# Keywords below used by higher level tests. Notice how given/when/then/and
# prefixes can be dropped. And this is a commend.

A user has a valid account
    Create valid user    ${USERNAME}    ${PASSWORD}

She changes her password
    Change password    ${USERNAME}    ${PASSWORD}    ${NEW PASSWORD}
    Status should be    SUCCESS

She can log in with the new password
    Login    ${USERNAME}    ${NEW PASSWORD}

She cannot use the old password anymore
    Attempt to login with credentials    ${USERNAME}    ${PASSWORD}
    Status should be    Access Denied

用户自定义关键字可以包含其他用户关键字,或者是库关键字。正如你在本例中看到的那样,用户自定义关键字可以包含参数。它们还可以返回值,甚至是包括 FOR 循环。现在,重要的是,用户自定义关键字使得测试作者能过复用之前相同的步骤序列。用户自定义关键字也使得测试作者保持测试用例具有高可读性,并在不同场景下使用合适的抽象级别。

变量

定义变量

变量是 Robot Framework 的一个组成部分。通常在测试中使用的任何数据,如有更改,最好定义为变量。变量的定义语法非常简单,如下变量表所示:

*** Variables ***
${USERNAME}               janedoe
${PASSWORD}               J4n3D0e
${NEW PASSWORD}           e0D3n4J
${DATABASE FILE}          ${TEMPDIR}${/}robotframework-quickstart-db.txt
${PWD INVALID LENGTH}     Password must be 7-12 characters long
${PWD INVALID CONTENT}    Password must be a combination of lowercase and uppercase letters and numbers

变量还可以由命令行给出,这对不同环境中执行测试用例是非常有用的。例如,可以如下执行演示程序:

pybot --variable USERNAME:johndoe --variable PASSWORD:J0hnD0e QuickStart.rst

除用户定义的变量外,有一些是始终可用的内置变量。这些变量包括 ${TEMPDIR} 和 ${/},这在上述示例中被使用。

使用变量

变量可以在测试数据的大多数地方使用。他们最常被用作关键字的参数,像下面的测试用例演示的那样。返回值分配给变量和以后使用。例如,如下的 Database Should Contain 用户关键字,将数据库内容设置在 ${database} 变量中,然后使用 BuiltIn 关键字 Should Contain 来验证内容的正确性。库和用户关键字都能返回值。

*** Test Cases ***
User status is stored in database
    [Tags]    variables    database
    Create Valid User    ${USERNAME}    ${PASSWORD}
    Database Should Contain    ${USERNAME}    ${PASSWORD}    Inactive
    Login    ${USERNAME}    ${PASSWORD}
    Database Should Contain    ${USERNAME}    ${PASSWORD}    Active

*** Keywords ***
Database Should Contain
    [Arguments]    ${username}    ${password}    ${status}
    ${database} =     Get File    ${DATABASE FILE}
    Should Contain    ${database}    ${username}\t${password}\t${status}\n

组织测试用例

测试套件

测试用例的集合被称为 Robot Framework 中的测试套件。每个输入文件,该文件包含测试用例构成一个测试套件。当 执行本指南,你可以看到控制台输出的 QuickStart。这个名字就是从文件名中生成的,在报告和日志中也可以看到。

也可以通过层次结构来组织测试用例,将测试用例文件放入目录,再将这些目录放置到其他目录中。所有这些目录会自动创建高级测试套件,目录名即为测试套件的名字。由于测试套件只是文件和目录,它们可以很方便地放置在任何版本控制系统中。

安装和卸载

如果要在每个测试用例想在之前或之后要执行的某些关键字,可以在 Test Setup 和 Test Teardown 中设置。同样的,如果要在测试套件之前或之后执行某些关键字,你只需要在 Suite Setup 和 Suite Teardown 中设置。 单个测试也可以通过测试用例表格的 [Setup] 和 [Teardown] 使用自定义安装或卸载。这和之前 数据驱动测试 使用 [Template] 的方法是相同的。

在本演示中我们要确保在开始执行之前,每个测试之后,数据库被清除:

*** Settings ***
Suite Setup       Clear Login Database
Test Teardown     Clear Login Database

使用标签

Robot Framework 允许设置测试用例标签,赋予其元数据。标签可以 Force Tags 和 Default Tags 为文件中所有测试用例强制设置,如下表中的所有测试用例。还可以通过 [Tags] 为单个测试用例设置,比如 之前 的 User status is stored in database 测试。

*** Settings ***
Force Tags        quickstart
Default Tags      example    smoke

当你在执行完后查看报告,你可以看到每个测试用例有指定的标签,也可以看到针对每个标签生成的统计信息。标签也可以用于许多其他用途,其中最重要的是有选择地执行测试。你可以试试,比如下列命令:

pybot --include smoke QuickStart.rst
pybot --exclude database QuickStart.rst

创建测试库

Robot Framework 提供了一个简单的 API 来使用 Python 或 Java 创建测试库,一些远程库接口还允许使用其他编程语言。《Robot Framework 用户指南》包含有关库 API 的详细说明。

举例来说,我们来看一下本例中的 LoginLibrary 测试库。库位于 lib/LoginLibrary.py,源代码如下。通过源代码可以看到,关键字 Create User 如何映射到实际的执行方法 create_user。

import os.path
import subprocess
import sys


class LoginLibrary(object):

    def __init__(self):
        self._sut_path = os.path.join(os.path.dirname(__file__),
                                    '..', 'sut', 'login.py')
        self._status = ''

    def create_user(self, username, password):
        self._run_command('create', username, password)

    def change_password(self, username, old_pwd, new_pwd):
        self._run_command('change-password', username, old_pwd, new_pwd)

    def attempt_to_login_with_credentials(self, username, password):
        self._run_command('login', username, password)

    def status_should_be(self, expected_status):
        if expected_status != self._status:
            raise AssertionError("Expected status to be '%s' but was '%s'."
                                % (expected_status, self._status))

    def _run_command(self, command, *args):
        command = [sys.executable, self._sut_path, command] + list(args)
        process = subprocess.Popen(command, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        self._status = process.communicate()[0].strip()