快速开始

基本规范

seldom继承unittest单元测试框架,所以他的编写规范与unittestopen in new window基本保持一致。

# test_sample.py
import seldom


class YouTest(seldom.TestCase):

    def test_case(self):
        """a simple test case """
        self.assertEqual(1+1, 2)


if __name__ == '__main__':
    seldom.main()

基本规范:

  1. 创建测试类YouTest并继承seldom.TestCase类。
  2. 创建测试方法test_case, 必须以test开头。
  3. seldom.mian()是框架运行的入口方法,接下来详细介绍。

main() 方法

main()方法是seldom运行测试的入口, 它提供了一些最基本也是最重要的配置。

import seldom

# ...

if __name__ == '__main__':
    seldom.main(path="./",
                browser="chrome",
                base_url=None,
                app_info=None,
                app_server=None,
                report=None,
                title="百度测试用例",
                tester="虫师",
                description="测试环境:chrome",
                debug=False,
                rerun=0,
                language="en",
                timeout=10,
                whitelist=[],
                blacklist=[],
                open=True,
                extensions=None,
                failfast=False
                )

参数说明

  • path : 指定测试目录或文件, 与case参数互斥。
  • case : 指定测试用例, 与path参数互斥。
  • browser : 指定浏览器("chrome"、"firefox" 等), Web测试。
  • base_url : 设置全局的基本URL, HTTP测试。
  • app_info : app 启动信息,参考desired_capabilities配置, app测试。
  • app_server : appium server 启动地址(默认 http://127.0.0.1:4723), app测试。
  • report : 自定义测试报告的名称,默认格式为2020_04_04_11_55_20_result.html
  • title : 指定测试报告标题。
  • tester : 指定测试人员, 默认Anonymous
  • description : 指定测试报告描述。
  • debug : debug模式,设置为True不生成测试HTML测试,默认为False
  • rerun : 设置失败重新运行次数,默认为 0
  • language : 设置HTML报告中英文,默认en, 中文zh-CN
  • timeout : 设置超时时间,默认10秒。
  • whitelist : 用例标签(label)设置白名单。
  • blacklist : 用例标签(label)设置黑名单。
  • open : 是否使用浏览器自动打开测试报告,默认True
  • extensions: 加载扩展,appium使用。
  • failfast: 当执行到失败的用例时,停止执行,仅在 debug=True时有效。

confrun.py 配置文件

seldom 3.1.0 提供过了 confrun.py 用于配置运行环境。 配置函数与 seldom.main() 的参数一致。

在这个文件中可以定义运行相关的钩子函数。

"""
seldom confrun.py hooks function
"""
from appium.options.android import UiAutomator2Options


def start_run():
    """
    Test the hook function before running
    """
    ...


def end_run():
    """
    Test the hook function after running
    """
    ...


def browser():
    """
    Web UI test:
    browser: gc(google chrome)/ff(firefox)/edge/ie/safari
    """
    return "gc"


def base_url():
    """
    http test
    api base url
    """
    return "http://httpbin.org"


def app_info():
    """
    app UI test
    appium app config
    """
    capabilities = {
        "automationName": "UiAutomator2",
        "platformName": "Android",
        "appPackage": "com.meizu.flyme.flymebbs",
        "appActivity": "com.meizu.myplus.ui.splash.SplashActivity",
        "noReset": True,
    }
    options = UiAutomator2Options().load_capabilities(capabilities)
    return options


def app_server():
    """
    app UI test
    appium server/desktop address
    """
    return "http://127.0.0.1:4723"


def debug():
    """
    debug mod
    """
    return False


def rerun():
    """
    error/failure rerun times
    """
    return 0


def report():
    """
    setting report path
    Used:
    return "d://mypro/result.html"
    return "d://mypro/result.xml"
    """
    return None


def timeout():
    """
    setting timeout
    """
    return 10


def title():
    """
    setting report title
    """
    return "seldom test report"


def tester():
    """
    setting report tester
    """
    return "bugmaster"


def description():
    """
    setting report description
    """
    return ["windows", "jenkins"]


def language():
    """
    setting report language
    return "en"
    return "zh-CN"
    """
    return "en"


def whitelist():
    """test label white list"""
    return []


def blacklist():
    """test label black list"""
    return []


def mock_url():
    """
    Replace the fixed url with the mock url
    :return:
    """
    config = {
        "http://httpbin.org/get": "http://127.0.0.1:8000/api/data",
    }
    return config


def failfast():
    """Use case exe failed to stop, only support debug=True"""
    return False

以上配置根据需求自动化项目类型配置,相互可能冲突的钩子函数:

  • Web UI测试: browser()
  • http 接口测试: base_url()
  • app UI测试: app_info(), app_server()

参数表格:

seldom.main() (参数)confrun.py (函数)类型说明
pathN/A通用指定测试目录或文件, 与case参数互斥。
caseN/A通用指定测试用例, 与path参数互斥。
browserbrowser()Web指定web测试运行的浏览器。
base_urlbase_url()HTTP指定HTTP接口测试的基本URL。
app_infoapp_info()Appapp 启动信息,参考appium desired_capabilities配置, app测试。
app_serverapp_server()Appappium server 启动地址(默认 http://127.0.0.1:4723), app测试。
reportreport()通用自定义测试报告的名称,例如result.html/result.xml
titletitle()通用指定HTML报告标题。
testertester()通用指定HTML报告测试人员。
descriptiondescription()通用指定HTML报告描述。
languagelanguage()通用设置HTML报告中英文,默认en, 中文zh-CN
debugdebug()通用debug模式,设置为True不生成测试HTML测试,默认为False
rerunrerun()通用设置失败重新运行次数。
timeouttimeout()通用设置自动化全局超时时间,默认10秒。作用于元素定位、断言等。
whitelistwhitelist()通用用例标签(label)设置白名单。
blacklistblacklist()通用用例标签(label)设置黑名单。
openN/A通用是否使用浏览器自动打开测试报告,默认True
N/Amock_url()HTTP定义mock URL 映射。

运行测试

seldom 的运行有三种方式:

  • main() 方法:在.py 文件中使用seldom.main() 方法。
  • seldom 命令:通过sedom 命令指定要运行的目录&文件&类&方法。
  • pycharm右键执行:这种方式无法读取到配置,有严重缺陷。

强烈建议使用前两种!!

1. main()方法运行测试

  • 目录结构
mypro/
├── test_dir/
│   ├── __init__.py
│   ├── test_sample.py
└── run.py  # 运行配置文件

创建 test_sample.py 文件,在测试文件中使用main()方法,如下:

# test_sample.py
import seldom
from seldom import data


class YouTest(seldom.TestCase):

    def test_case(self):
        """a simple test case """
        self.assertEqual(1 + 1, 2)

    @data([
        ("case1", "seldom"),
        ("case2", "XTestRunner"),
    ])
    def test_ddt(self, name, search):
        """ ddt case """
        print(f"name: {name}, search_key: {search}")


if __name__ == '__main__':
    # 运行当前文件中的用例
    seldom.main()  # 默认运行当前文件中所有用例
    seldom.main(case="test_sample")  # 指定当前文件
    seldom.main(case="test_sample.YouTest")  # 指定测试类
    seldom.main(case="test_sample.YouTest.test_case")  # 指定测试用例

    # 使用参数化的用例
    seldom.main(case="test_sample.YouTest.test_ddt")  # 错误用法
    seldom.main(case="test_sample.YouTest.test_ddt_0")  # 正确用法,0表示第一条数据用例

创建 run.py 文件,用于全局的指定要运行的用例。

import seldom


if __name__ == '__main__':
    # 指定运行其他目录&文件
    seldom.main(path="./")  # 指定当前文件所在目录下面的用例。
    seldom.main(path="./test_dir/")  # 指定当前目录下面的test_dir/ 目录下面的用例。
    seldom.main(path="./test_dir/test_sample.py")  # 指定测试文件中的用例。
    seldom.main(path="D:/seldom_sample/test_dir/test_sample.py")  # 指定文件的绝对路径。

seldom.main() 提供哪些参数,请参考前面的文档。

  • 运行测试文件
> cd mypro/  # 进入项目根目录
> python ./test_dir/test_sample.py      # 运行指定测试文件
> python run.py      # 运行run.py文件

2. seldom命令执行

  • 目录结构
mypro/
├── test_dir/
│   ├── __init__.py
│   ├── test_sample.py
└── confrun.py  # 运行配置文件

seldom -p命令指定目录和文件。

seldom -m命令可以提供更细粒度的运行。

> cd mypro/  # 进入项目根目录
> seldom -p test_dir  # 运行目录
> seldom -p test_dir/test_sample.py  # 运行文件
> seldom -m test_dir.test_sample       # 运行文件
> seldom -m test_dir.test_sample.YouTest # 运行 SampleTest 测试类
> seldom -m test_dir.test_sample.YouTest.test_case # 运行 test_case 测试方法

运行相关的配置,可以在confrun.py 文件中配置。

3. 在pyCharm中运行测试

强烈不建议这种方式,除非你的测试用例没有任何依赖。

步骤一:配置测试用例通过 unittest 运行。

步骤二:在文件中选择测试类或用例执行。

警告:运行用例打开的浏览器,需要手动关闭, seldom不做用例关闭操作。

失败重跑

Seldom支持错误&失败重跑。

# test_sample.py
import seldom


class YouTest(seldom.TestCase):

    def test_error(self):
        """error case"""
        self.assertEqual(a, 2)

    def test_fail(self):
        """fail case """
        self.assertEqual(1 + 1, 3)


if __name__ == '__main__':
    seldom.main(rerun=3)

参数说明:

  • rerun: 指定重跑的次数,默认为 0

运行日志:

> python test_sample.py


              __    __
   ________  / /___/ /___  ____ ____
  / ___/ _ \/ / __  / __ \/ __ ` ___/
 (__  )  __/ / /_/ / /_/ / / / / / /
/____/\___/_/\__,_/\____/_/ /_/ /_/  v3.x.x
-----------------------------------------
                             @itest.info




XTestRunner Running tests...

----------------------------------------------------------------------
ERetesting... test_error (test_sample.YouTest)..1
ERetesting... test_error (test_sample.YouTest)..2
ERetesting... test_error (test_sample.YouTest)..3
EFRetesting... test_fail (test_sample.YouTest)..1
FRetesting... test_fail (test_sample.YouTest)..2
FRetesting... test_fail (test_sample.YouTest)..3
Generating HTML reports...
F2022-07-12 00:22:52 log.py | SUCCESS | generated html file: file:///D:\github\seldom\reports\2022_07_12_00_22_51_result.html
2022-07-12 00:22:52 log.py | SUCCESS | generated log file: file:///D:\github\seldom\reports\seldom_log.log

测试报告

seldom 默认生成HTML测试报告,在运行测试文件下自动创建reports目录。

  • 运行测试用例前
mypro/
└── test_sample.py
  • 运行测试用例后
mypro/
├── reports/
│   ├── 2020_01_01_11_20_33_result.html
│   ├── seldom_log.log
└── test_sample.py

通过浏览器打开 2020_01_01_11_20_33_result.html 测试报告,查看测试结果。

debug模式

如果不想每次运行都生成HTML报告,可以打开debug模式。

import seldom

seldom.main(debug=True)

定义测试报告

import seldom

seldom.main(report="./report.html",
            title="百度测试用例",
            tester="虫师",
            description="测试环境:windows 10/ chrome")
  • report: 配置报告名称和路径。
  • title: 自定义报告的标题。
  • tester: 指定自动化测试工程师名字。
  • description: 添加报告信息,支持列表, 例如:["OS: windows","Browser: chrome"]。

XML测试报告

如果需要生成XML格式的报告,只需要修改报告的后缀名为.xml即可。

import seldom

seldom.main(report="report.xml")

多线程运行

多线程无疑可以缩短用例的运行时间,一般由两种方式实现。

  1. 设置线程数,交由框架去分配用例,或按照测试用例、测试类、测试模块分配给线程执行。
  • 优点:简单,例如 pytest-xdist ,只需要指定 线程数 即可。
  • 缺点:无法控制用例的拆分粒度,如果在设计用例时,不同的用例有依赖,刚好被分到的不同的线程,那么必定导致用例失败。
  1. 自己分好线程,分别调用框架执行。
  • 优点:手动划分线程,可以按照目录、文件、甚至测试类或方法 拆分线程。
  • 缺点:首先会比较麻烦,而且多个线程的执行结果无法很好的合并到一起。

seldom 推荐第二种方法,把线程的划分方式交给用户,无疑是更灵活的方法。至于报告的合并统计就每有什么好办法了。

  • 用例维度使用多线程。
import seldom
from seldom.extend_lib import threads


class MyTest(seldom.TestCase):

    def test_baidu(self):
        self.open("https://www.baidu.com")
        self.sleep(3)

    def test_bing(self):
        self.open("https://www.bing.com")
        self.sleep(4)


@threads(2)  # !!!核心!!!! 设置线程数
def run_case(case: str, browser: str):
    """
    根据传入的case执行用例
    """
    seldom.main(case=case, browser=browser, debug=True)


if __name__ == "__main__":
    # 将两条用例拆分,分别用不同的浏览器执行
    cases = {
        "test_thread_case.MyTest.test_baidu": "chrome",
        "test_thread_case.MyTest.test_bing": "edge"
    }

    for key, value in cases.items():
        run_case(key, value)
  • 目录或文件维度使用多线程。
import seldom
from seldom.extend_lib import threads


@threads(3)  # !!!核心!!!! 设置线程数
def run_case(path: str):
    """
    根据传入的path执行用例
    """
    seldom.main(path=path, debug=True)


if __name__ == "__main__":
    # 定义3个测试文件,分别丢给3个线程执行。
    paths = [
        "./test_dir/more_case/test_case1.py",
        "./test_dir/more_case/test_case2.py",
        "./test_dir/more_case/test_case3.py"
    ]
    for p in paths:
        run_case(p)