测试开发

kunlun-data文件夹

首次安装Kunlun平台后, 执行 kunlun start , 平台将自动创建 C:\kunlun-data 文件夹, 用户也可以自行创建此文件夹。

C:\kunlun-data 包含以下文件或文件夹.

  1. db.sqlite3 :数据库文件, 保存测试记录.

  2. logs :测试日志文件夹, 保存测试过程中产生的测试日志, 以及保存一些临时文件.

  3. tftpboot : tftp 服务与 ftp 服务的根文件目录.

定制用户界面

Win10用户可以通过以下方式将 测试程序示例 下载到 C:\kunlun-data 文件夹

cd C:\kunlun-data
git clone https://gitee.com/robinwu0607/kunlun_scripts
  • configs 中包含界面定制代码.

  • demo 中包含演示测试代码.

../_images/hostname.png

用于演示的 autotest.py 文件的内容示例如下, 用户可以根据业务需要进行增加、修改或是拷贝:

# 导入接口函数库
from kunlun import lib

# 函数名必须是 main()
def main():
    # 从lib.get_configuration()获取根实例kun
    # tftp: 为True时将启动tftp服务, 默认为False
    # ftp: 为True时将启动ftp服务, 默认为False
    kun = lib.get_configuration(tftp=False, ftp=False)
    # add_station: 在根实例Kl下增加一个Station工站
    # name: 工站的名称
    # desc: 工站的描述说明
    # recycle: 为True时, Container测试PASS后, 将会自动重新开始测试, 免人工反复点击开始按钮.默认值为False
    # image: 工站的封面图片.demo是项目名称, 建议图片的格式为.jpg或是.png
    station = kun.add_station(name="PCBA", desc="PCBA功能测试", recycle=False, image=r"demo\bee.jpg", dense=True)
    # 设置工站的测试任务所关联的用户代码路径.
    # demo是项目名称, sequence是文件, 平台将调用sequence.py下的函数main_sequence()作为入口函数
    station.add_sequence("demo.pcba")
    station.add_connection(name="UDS", protocol="DUMMY")
    # 设置工站的硬件参数, 用户在代码中可以使用lib.get_params("host")获取到参数的值
    station.add_params_data(host="192.168.0.1", password="root")
    # 用循环的方式连续添加Container工位
    for i in range(4):
        # add_container: 在工站实例station下增加一个Container工位.
        # name: 工位的名称,  工位的名称动态累加
        container = station.add_container(name="UUT{:02d}".format(i))
        # 设置工位的硬件参数.工位会继承工站的硬件参数, 同时可以覆盖工站的参数, 或是新增专用的参数.
        container.add_params_data(ip="192.168.1.{}".format(i), test="hello world")
        # 增加一个Connection连接, 连接的协议为DUMMY, 将调用windows的cmd.exe窗口.
        container.add_connection(name="UUT", protocol="DUMMY")
        # 增加一个Connection连接, 连接的协议为SSH, 目标主机ip为192.168.0.2, 登入用户名为root, 密码为root.
        container.add_connection(name="SSH", protocol="SSH", host="192.168.0.2", username="root", password="root", prompt="$")
        # 增加一个Connection连接, 连接的协议为TELNET, 目标主机ip为192.168.0.3, 端口为2009.
        container.add_connection(name="TNT", protocol="TELNET", host="192.168.0.3", port=2009, username="root", password="root", prompt="$")
        # 增加一个Connection连接, 连接的协议为SERIAL, 目标端口为COM1, 波特率为115200.
        container.add_connection(name="SER", protocol="SERIAL", serial="COM1", baudrate=115200)
        # 增加MEASURE窗口
        container.show_measure_panel()
    # 在根实例Kl下增加第二个Station工站
    station = kun.add_station(name="FCT", desc="FCT测试", recycle=False)
    station.add_sequence("demo.fct")
    for i in range(4):
        container = station.add_container(name="UUT{:02d}".format(i))
        container.add_connection(name="UUT", protocol="DUMMY")
        # 增加MEASURE窗口
        container.show_measure_panel()
        # 增加STEP窗口
        container.show_step_panel()
        # 增加INFO窗口
        container.show_info_panel()

需要注意的是:每次对 autotest.py 进行修改, 必须使用 kunlun start 重启服务, 修改后的内容才会生效.

需要再次强调的是, 平台会根据 add_sequence() 中的入参调用对应项目下的 .py 文件的 main_sequence() 函数作为入口函数.

开发者模式与生产模式

在测试主界面, 用户可以自由切换 生产模式开发模式 (默认为 生产模式 ), 平台后台将根据选择的模式, 自动处理调用 kunlun_scripts 或是 develop 文件夹下测试代码.

../_images/product-mode.png ../_images/develop-mode.png

需要强调的是:每次页面刷新后, 测试模式将会默认切换至 生产模式 .

测试服务

平台提供 FTPTFTP 服务.

FTP服务

用户在定制界面时, 若设置 lib.get_configuration(ftp=True) , 启动平台时, FTP服务也将启动.

FTP的默认文件夹是 C:\kunlun-data\tftpboot , 用户名密码为: autotest/autotest .

TFTP服务

用户在定制界面时, 若设置 lib.get_configuration(tftp=True) , 启动平台时, TFTP服务也将启动.

FTP的默认文件夹是 C:\kunlun-data\tftpboot .

需要注意的是:

  1. lib.get_configuration(ftp=True, tftp=True) 时, FTP服务与TFTP服务将同时启动, 不冲突.

  2. FTP服务 使用端口 21, TFTP服务 使用端口 69.

测试代码开发方案

用户的代码存放于 C:\kunlun-data\kunlun_scripts 或是 C:\kunlun-data\develop 下.

给开发者建议:

  1. 用户项目下的文件夹与文件, Python代码开发规范参考PEP8规范, PEP8链接为: https://www.python.org/dev/peps/pep-0008/

  2. 用户项目下的每一个文件夹, 都包含文件 __init__.py .

  3. 用户项目文件夹的名称与实际项目名称保持一致.

  4. 使用 git 等版本工具管理测试代码.

基于不同的测试需求与目标, 用户可以采用快速开发方案或是测试序列编排方案.

小型项目快速开发

建议采用快速开发方案的是:

  1. 相对独立的项目(较少与其他项目复用代码).

  2. 测试命令行小于或等于20条的项目.

快速开发方案的演示代码可以参考 C:\kunlun-data\kunlun_scripts\demo\fct.py .

以下截取部分代码片段进行说明:

from kunlun import lib
import time

# 获取事件日志接口
log = lib.get_event_logger()


# 定义入口函数, 每个文件只有一个入口函数.
def main_sequence():
    log.debug("welcome to main sequence")
    container_name = lib.get_container_name()
    log.debug(container_name)
    ans = lib.ask_question("输入序列号:")
    log.debug("ask_question: [%s]", ans)
    ans = lib.ask_question("选择产品类型:", options=["12-3456-78", "98-7654-32"])
    log.debug("ask_question choose: (%s)", ans)
    ans = lib.ask_question("选择产品类型:", options=["12-3456-78", "98-7654-32"], multiple=True)
    log.debug("ask_question multiple choose: (%s)", ans)
    ans = lib.ask_questions(["输入序列号:", "输入MAC:", "输入编码:"])
    log.debug("ask_questions: (%s)", ans)
    lib.ud.hello = "good"
    uut = lib.conn.UUT
    uut.open()
    log.debug("run test1: dir")
    uut.send("dir\r", expect=">", timeout=10)
    time.sleep(1)
    log.debug(lib.get_params())
    lib.add_test_data(sernum="1234567890", uuttype="DEMO", area="DEMO")
    run_test2()
    run_test4()
    return


def run_test2():
    uut = lib.conn.UUT
    uut.open()
    log.debug("run test2")
    uut.send("dir\r", expect=">", timeout=10)
    if "UUT01" in lib.get_container_name():
        lib.set_step_name("Test Step2")
        raise Exception("run test failed")
    time.sleep(2)
    return


def run_test4():
    lib.set_display1("SERNUM: 1234567890")
    lib.set_display2("UUTTYPE: 12-3456-78")
    log.debug("userdict1: {}".format(lib.ud.hello))
    log.debug("userdict2: {}".format(lib.ud))
    return

大型项目测试编排

建议采用测试序列编排方案的是:

  1. 大型项目的子项目(较多与其他项目复用代码).

  2. 持续演进的项目.

  3. 需要实施较多测试策略的项目.

  4. 测试命令行大于20条的项目.

测试序列编排方案的演示代码可以参考 C:\kunlun-data\kunlun_scripts\demo\pcba.py .

以下截取部分代码片段供参考:

from kunlun import lib
import time

# 获取事件日志接口
log = lib.get_event_logger()


# 定义入口函数, 每个文件只有一个入口函数.
def main_sequence():
    # 增加一个根测试序列sequence, 并设置名称为MAIN SEQ
    seq = lib.get_sequence_definition("MAIN SEQ")
    # 根序列增加一个step, 并设置此step名称为RUN TEST, 如果测试失败, 则失败项为RUN TEST
    seq.add_step(run_test, name="RUN TEST")
    # 根序列增加一个step, 并传参数kws给step
    seq.add_step(run_test1, name="run test1", kws={"name": "gps test", "value": "cpu test"})
    # 根序列增加一个子测试序列, 并设置名称为SUB MAIN SEQ
    # in_parallel: 并行测试标识, 为True时, 将与 下一个step/测试序列 并行执行
    # 测试循环次数设置为3次
    sub_seq = seq.add_sequence("SUB MAIN SEQ", in_parallel=True, cycle_count=1)
    # 子序列增加一个step
    sub_seq.add_step(run_test2, name="run test2-1")
    # 子序列增加一个step
    sub_seq.add_step(run_test2, name="run test2-2")
    # 根序列增加一个step
    # 测试循环时间为30秒
    seq.add_step(run_test3, name="RUN TEST3", cycle_time=1)
    # 根序列增加一个step.
    seq.add_step(run_test4, name="run TEST4")
    # 返回根序列
    return seq


def run_test():
    log.debug("welcome to main sequence")
    container_name = lib.get_container_name()
    log.debug(container_name)
    ans = lib.ask_question("输入序列号:")
    log.debug("ask_question: [%s]", ans)
    ans = lib.ask_question("选择产品类型:", options=["12-3456-78", "98-7654-32"])
    log.debug("ask_question choose: (%s)", ans)
    ans = lib.ask_question("选择产品类型:", options=["12-3456-78", "98-7654-32"], multiple=True)
    log.debug("ask_question multiple choose: (%s)", ans)
    ans = lib.ask_questions(["输入序列号:", "输入MAC:", "输入编码:"])
    log.debug("ask_questions: (%s)", ans)
    lib.ud.hello = "good"
    return


def run_test1(name="name", value="value"):
    log.debug("name: {}".format(name))
    log.debug("value: {}".format(value))
    uut = lib.conn.UUT
    uut.open()
    log.debug("run test1: dir")
    uut.send("dir\r", expect=">", timeout=10)
    time.sleep(1)
    # log.debug(lib.get_json_params("Sheet1", "param1"))
    # log.debug(lib.get_json_params("Sheet1", "param*"))
    # log.debug(lib.get_json_params("Sheet1", "*_test"))
    # log.debug(lib.get_json_params("Sheet1", "*"))
    log.debug(lib.get_params())
    return


def run_test2():
    uut = lib.conn.UUT
    log.debug("run test2")
    uut.send("dir\r", expect=">", timeout=10)
    if "fail" in uut.buf:
        raise Exception("run test failed")
    time.sleep(2)
    return


def run_test3():
    log.debug("run test3")
    time.sleep(2)
    return


def run_test4():
    lib.set_display1("SERNUM: 1234567890")
    lib.set_display2("UUTTYPE: 12-3456-78")
    lib.add_test_data(sernum="1234567890", uuttype="DEMO", area="DEMO")
    log.debug("userdict1: {}".format(lib.ud.hello))
    log.debug("userdict2: {}".format(lib.ud))
    return

需要说明的是: lib.get_sequence_definition() 中可以承载较多的测试策略, 用户可以将精力投入到测试业务代码本身, 聚焦更有价值的部分, 测试策略交给平台处理.

需要注意的是: 入口函数 main_sequence() 必须 return 根测试序列 seq 给到平台, 否则平台将无法执行测试序列.

导入其他pip依赖包

直接使用 pip install 安装对应依赖包即可.

命令行kunlun run

使用命令行执行测试用例, 方便在测试程序发布前, 快速单独验证测试代码, 及时发现代码问题:

Usage: kunlun run [prodcut] [case] [--param] [--key value] [--develop] [--help]

kunlun run --help 可以查看帮助信息. kunlun run [prodcut] [case] --param --key value 可以注入参数到测试用例。 kunlun run [prodcut] [case] --develop 可以调用开发模式下的测试用例。

以项目 demo 为例, 首先需要在项目下创建文件夹 testcase, 得到 demo/testcase/.

demo 代码中可用的示例:

kunlun run demo test_demo

TestCase的写法:

  1. class类结构.

class类结构式测试用例

demo/testcase/ 文件夹下创建 .py 文件, 文件中创建 class TestCase(lib.TestCase) 类, 需要继承 lib.TestCase .

运行中, 平台会调用 setup(), 再调用 run(), 无论结果是PASS还是FAIL, 最后调用 teardown(), 测试用例如下:

from kunlun import lib

import time

# 获取事件日志接口
log = lib.get_event_logger()


class TestCase(lib.TestCase):
    """This is the Test Case document."""
    uut = None

    def __init__(self) -> None:
        super().__init__()

    def setup(self):
        log.info("it is setup")
        # add temp connection for use.
        lib.add_connection(name="SSH",
                        protocol="SSH",
                        host="172.28.247.192",
                        username="lenovo",
                        password="lenovo",
                        prompt="$")
        self.uut = lib.conn.SSH
        self.uut.open()

    def teardown(self):
        log.info("it is teardown")
        self.uut.close()

    def run(self):
        log.info("run demo cli")
        for i in range(3):
            self.uut.send("pwd\r", expect=[">", "$"], timeout=30)
            time.sleep(2)

需要说明的是, 方法 setup() , run()teardown() 都不能定义入参.

菜单栏kunlun menu

使用指令 kunlun menu 可以进入菜单栏模式。

菜单栏模式是 kunlun run 的另外一种表现形式, 方便用户使用, 避免记忆复杂的指令集.

kunlun run 中的 class类结构式测试用例 中,如果定义了方法 menu_list(), 即可以在 kunlun menu 中体现.

具体用法参考: demo\testcase\test_demo.py . 以下是 demo 代码中的示例:

def menu_list(self):
    return [
        ("kunlun run demo test_demo", "执行DEMO程序1"),
        ("kunlun run demo test_demo", "执行DEMO程序2"),
    ]


kunlun-autotest> kunlun menu
|--------------------------------------------------------|
|---------------- KunLun AutoTest MENU ------------------|
|--------------------------------------------------------|
current avaiable project:
0 -- go to upper menu
1 -- demo
ctrl_c -- exit

Input your choice: 1
|--------------------------------------------------------|
|---------------- KunLun AutoTest MENU ------------------|
|--------------------------------------------------------|
demo avaiable case:
0 -- go to upper menu
1 -- test_demo
2 -- test_demo2
ctrl_c -- exit

Input your choice: 1
|--------------------------------------------------------|
|---------------- KunLun AutoTest MENU ------------------|
|--------------------------------------------------------|
demo test_demo avaiable item:
0 -- go to upper menu
1 -- 执行DEMO程序1
2 -- 执行DEMO程序2
3 -- 执行DEMO程序3
4 -- 执行DEMO程序4
5 -- 执行DEMO程序5
6 -- 执行DEMO程序6
7 -- 执行DEMO程序7
8 -- 执行DEMO程序8
9 -- 执行DEMO程序9
10 -- 执行DEMO程序10
11 -- 执行DEMO程序11
ctrl_c -- exit

Input your choice:

图形化编程 Graphical Programming

尝试引入图形化编程来简化测试开发, 当前图形化编程只能完成简易的测试开发, 后续再慢慢完善.

_static/测试开发/gp-menu.png

通过点击菜单进入对应图形化编程界面.

用户可以在以下地址下载测试程序示例.

https://gitee.com/robinwu0607/kunlun_scripts

Configs Editor

Configs Editor用来定制测试界面, 文件保存在 configs 中.

_static/测试开发/gp-configs.png

TestCase Editor

TestCase Editor用来开发测试用例, 文件保存在 demo/testcase 中。

_static/测试开发/gp-testcase.png

Sequence Editor

Sequence Editor用来开发测试序列, 文件保存在 demo/sequence 中。

_static/测试开发/gp-sequence.png