Skip to content

lujiatao2/testauto

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

  自动化测试框架,支持多线程和参数化测试,且自带HTML测试报告!

主要特性

  • 支持IDE和命令行方式执行测试用例。
  • 支持多线程测试。
  • 支持参数化测试。
  • 自带HTML测试报告。
  • 支持设置终止策略、重试策略和超时时间。
  • 易扩展,开发者可自定义测试任务、测试记录器和测试执行器等。

安装

在线安装

  执行以下命令即可安装testauto的最新版本:

pip install test-auto

  如需安装指定版本,比如安装1.0.0版本,可执行以下命令:

pip install test-auto==1.0.0

离线安装

  如果已经下载了test_auto-X.X.X-py3-none-any.whl或test-auto-X.X.X.tar.gz安装文件,那么可以使用离线安装方式安装testauto。
  你可以在PyPI的以下地址找到安装文件:

https://pypi.org/project/test-auto/#files

  或者GitHub的以下地址找到安装文件:

https://github.com/lujiatao2/testauto/releases

  若使用test_auto-X.X.X-py3-none-any.whl安装文件,执行以下命令即可安装testauto:

pip install path/to/test_auto-X.X.X-py3-none-any.whl

  若使用test-auto-X.X.X.tar.gz安装文件,执行以下命令即可安装testauto:

pip install path/to/test-auto-X.X.X.tar.gz

第一个测试用例

  来开始使用testauto编写第一个测试用例吧!

# case_for_doc.py
from testauto import main
from testauto.case import TestCase


class TestCase01(TestCase):

    def test_case(self):
        ...


if __name__ == '__main__':
    main()

  执行结果如下:

======================================================================================================================================================
执行总数:1
开始时间:2021-01-24 14:48:21    结束时间:2021-01-24 14:48:21    执行耗时:00时00分00秒
------------------------------------------------------------------------------------------------------------------------------------------------------
执行结果      数量        百分比(%)
通过        1         100.0
失败        0         0.0
阻塞        0         0.0
超时        0         0.0
未执行       0         0.0
======================================================================================================================================================

测试用例

基本用法

  测试用例使用一个Python类来表示,且该类必须直接或间接继承至TestCase抽象基类才能被testauto识别为测试用例。

class TestCase02(TestCase):

    def test_case(self):
        print(self.__class__.__name__)


class TestCase03(TestCase02):

    def test_case(self):
        print(self.__class__.__name__)


class TestCase04:

    def test_case(self):
        print(self.__class__.__name__)

  执行以上代码后,可以看到只有TestCase02和TestCase03的类名被打印了。

初始化和清理操作

  测试用例支持添加初始化和清理操作,只需重写setup()和teardown()方法即可。

class TestCase05(TestCase):

    def setup(self):
        print('这是初始化操作')

    def test_case(self):
        print('这是测试用例')

    def teardown(self):
        print('这是清理操作')

  执行结果如下:

这是初始化操作
这是测试用例
这是清理操作

测试用例属性

  • project:测试工程名称,默认为:Default Project。
  • module:测试模块名称,默认为:Default Module。
  • title:测试用例标题,默认为:Default Title。
  • description:测试用例描述,默认:无。
  • priority:测试用例优先级,默认:P0。
  • designer:测试用例设计者,默认:Anonymous。
  • version:测试用例版本号,默认:1.0.0。
  • completed:测试用例完成状态,默认:已完成。

  以上属性都可以修改,只需在测试用例中显式声明即可。

class TestCase06(TestCase):
    title = '登录成功'
    priority = TestCasePriority.P1
    completed = False

    def test_case(self):
        ...

  从以上代码可以看出:测试用例优先级使用枚举表示,而不是字符串;而测试用例完成状态是一个布尔类型。

测试任务

基本用法

  testauto的执行单元是测试任务,而不是测试用例,即使只有一个测试用例,testauto也会自动将该测试用例转换为测试任务。
  为什么需要测试任务?比如一个测试项目有1000个测试用例,但对于不同的测试策略(冒烟测试?回归测试?),需要执行的测试用例显然是不同的,因此我们可以创建不同的测试任务。

# task_for_doc.py
from testauto.task import DefaultTestTask

from testauto_test.case_for_doc import *

# 冒烟测试用例对应的测试任务
test_task_01 = DefaultTestTask()
test_task_01.add_test_cases(TestCase01(), TestCase02())

# 回归测试用例对应的测试任务
test_task_02 = DefaultTestTask()
test_task_02.add_test_cases(TestCase01(), TestCase03())

if __name__ == '__main__':
    main(test_task=test_task_01)  # 执行冒烟测试
    main(test_task=test_task_02)  # 执行回归测试

  以上代码中的测试用例是上一节case_for_doc.py中的测试用例。

添加测试用例

  除了上一小节介绍的add_test_cases()方法外,还支持多种添加测试用例方法,以下为完整列表:

  • add_test_case():添加单个测试用例。
  • add_test_cases():添加多个测试用例。
  • add_test_cases_by_classes():通过类名增加测试用例,格式:path.to.module.Class
  • add_test_cases_by_modules():通过模块名增加测试用例,格式:path.to.module
  • add_test_cases_by_files():通过文件增加测试用例,格式:
    Windows:E:\path\to\dictionary\module.py
    macOS/Linux:/path/to/dictionary/module.py
  • add_test_cases_by_paths():通过路径增加测试用例,格式:
    Windows:E:\path\to\dictionary
    macOS/Linux:/path/to/dictionary

  以下演示add_test_cases_by_files()方法的使用:

test_task_03 = DefaultTestTask()
test_task_03.add_test_cases_by_files(
    r'E:\Software_Testing\Software Development\Python\PycharmProjects\testauto\testauto_test\case_for_doc.py')

if __name__ == '__main__':
    main(test_task=test_task_03)

  除了添加,也可以删除测试用例,对应的方法是remove_test_case()和remove_test_cases()。

过滤器

  过滤器是一个抽象类TestCaseFilter,它的作用是过滤掉不满足要求的测试用例。testauto内置了2个过滤器:

  • TestCaseCompletedShouldBe:根据测试用例完成状态来过滤测试用例。
  • TestCasePriorityShouldBe:根据测试用例优先级来过滤测试用例。

  比如我们只想执行P0优先级的测试用例,可以这么做:

test_task_03 = DefaultTestTask()
test_task_03.add_test_cases_by_files(
    r'E:\Software_Testing\Software Development\Python\PycharmProjects\testauto\testauto_test\case_for_doc.py')
test_task_03.add_filter(TestCasePriorityShouldBe(OperationMethod.EQUAL, TestCasePriority.P0))
test_task_03.filter_test_cases()

if __name__ == '__main__':
    main(test_task=test_task_03)

  从以上代码可以看出:在添加了过滤器后,要真正执行过滤操作,需要调用filter_test_cases()方法。
  除了添加,也可以删除过滤器,对应的方法是remove_test_filter()和remove_test_filters()。
  另外,DefaultTestTask默认包含TestCaseCompletedShouldBe过滤器,且过滤条件为OperationMethod.EQUAL和True,即测试用例完成状态等于True的测试用例才会被保留。当然你可以调用clear_test_task() 方法来阻止该行为,该方法会清空测试任务中的所有测试用例和过滤器。

命令行执行

  testauto提供了命令行执行的功能,执行以下命令获取帮助信息:

python -m testauto -h

  执行结果如下:

usage: testauto [-h] [-m [TEST_MODULES [TEST_MODULES ...]]] [-t TEST_TASK]
                [-r TEST_RECORDER] [-rn TEST_RUNNER] [-s STOP_STRATEGY]
                [-rt RETRY_STRATEGY] [-to TIMEOUT] [-p PARALLEL]

optional arguments:
  -h, --help            显示帮助信息。
  -m [TEST_MODULES [TEST_MODULES ...]], --test-modules [TEST_MODULES [TEST_MODULES ...]]
                        测试模块。示例:-m E:\path\to\dictionary\module.py
  -t TEST_TASK, --test-task TEST_TASK
                        测试任务。示例:-t path.to.module.callable,其中callable为返回TestTask对象的可调用对象。
  -r TEST_RECORDER, --test-recorder TEST_RECORDER
                        测试记录器。示例:-r path.to.module.callable,其中callable为返回TestRecorder对象的可调用对象。
  -rn TEST_RUNNER, --test-runner TEST_RUNNER
                        测试执行器。示例:-rn path.to.module.callable,其中callable为返回TestRunner对象的可调用对象。
  -s STOP_STRATEGY, --stop-strategy STOP_STRATEGY
                        终止策略。参数取值:0-全部完成(默认)/1-第一个未执行成功/2-第一个P0测试用例未执行成功
  -rt RETRY_STRATEGY, --retry-strategy RETRY_STRATEGY
                        重试策略。参数取值:0-不重试(默认)/1-立即重新执行测试用例/2-最后重新执行测试用例
  -to TIMEOUT, --timeout TIMEOUT
                        超时时间(单位秒)。参数取值:正整数
  -p PARALLEL, --parallel PARALLEL
                        并行执行数量。参数取值:正整数

  以上帮助信息已经把使用方法说明得很清楚了,以下演示如何使用命令行执行测试任务,新增测试任务test_task_04:

def test_task_04():
    test_task = DefaultTestTask()
    test_task.add_test_cases(TestCase01(), TestCase02())
    return test_task

  注意test_task_04要定义为可调用对象,这里定义为了一个函数。
  执行以下命令可运行test_task_04中的测试用例:

python -m testauto -t testauto_test.test_task.test_task_04

  另外,IDE和命令行均支持直接传入测试模块,但此时若同时传入了测试任务,testauto会忽略测试任务,而使用传入的测试模块自动创建测试任务。

多线程测试

  testauto支持多线程测试,使用方法很简单,通过main()方法传入parallel参数或命令行传入-p/--parallel参数即可。
  为了演示多线程测试带来的效率上优势,我们首先在case_for_doc.py中新增以下两个测试用例:

class TestCase07(TestCase):

    def test_case(self):
        sleep(5)


class TestCase08(TestCase):

    def test_case(self):
        sleep(5)

  然后在task_for_doc.py中新增test_task_05:

test_task_05 = DefaultTestTask()
test_task_05.add_test_cases(TestCase07(), TestCase08())

if __name__ == '__main__':
    main(test_task=test_task_05)

  如果直接执行test_task_05,那么测试耗时是10秒,这时我们加入parallel参数,并将参数值设置为2:

if __name__ == '__main__':
    main(test_task=test_task_05, parallel=2)

  重新执行test_task_05,可以看到耗时为5秒。
  需要注意的是,多线程执行测试用例时,需考虑线程安全性,如果多个测试用例同时对一个资源进行修改,会造成意想不到的结果。

参数化测试

  testauto使用@parameterized装饰器提供对参数化的支持。
  首先在case_for_doc.py中新增以下测试用例:

@parameterized(
    ('username', 'password'),
    [
        ('zhangsan', 'zhangsan123456'),
        ('lisi', 'lisi123456')
    ]
)
class TestCase09(TestCase):

    def test_case(self):
        username = self.get_param_value('username')
        password = self.get_param_value('password')
        print(f'我的用户名是:{username},我的密码是:{password}!')

  @parameterized装饰器的第一个参数是一个字符串类型的元组,每个字符串代表一个参数,可传递多个参数;第二个参数是一个元组组成的列表,每个元组代表一组参数,元组中参数的个数必须与参数数量一致。然后在测试用例中通过get_param_value() 方法来获取参数。
  在test_task.py中新增test_task_06:

test_task_06 = DefaultTestTask()
test_task_06.add_test_cases_by_classes('testauto_test.case_for_doc.TestCase09')

if __name__ == '__main__':
    main(test_task=test_task_06)

  注意test_task_06添加测试用例的方式不是直接传递测试用例对象,因为参数化测试时,需要根据参数的数量来动态创建测试用例对象,这个过程是testauto自动完成的。
  执行结果如下:

我的用户名是:zhangsan,我的密码是:zhangsan123456!
我的用户名是:lisi,我的密码是:lisi123456!

测试报告

  testauto有5种测试结果:

  • 通过:未引发任何异常。
  • 失败:引发AssertionError异常。
  • 超时:引发TimeoutError异常。
  • 阻塞:引发其它异常(非AssertionError和TimeoutError异常)。
  • 未执行:未执行。

  testauto内置的测试记录器DefaultTestRecorder会生成HTML测试报告。
  为了查看不同测试结果在测试报告中的显示效果,在case_for_doc.py中新增以下3个测试用例:

class TestCase010(TestCase):
    module = 'TestCase010模块'
    title = 'TestCase010标题'

    def test_case(self):
        sleep(1)


class TestCase11(TestCase):
    module = 'TestCase11模块'
    title = 'TestCase11标题'

    def test_case(self):
        sleep(2)
        assert False


class TestCase12(TestCase):
    module = 'TestCase12模块'
    title = 'TestCase12标题'

    def test_case(self):
        sleep(3)
        raise RuntimeError('我是TestCase12,这是我抛的异常!')

  然后直接执行test_task_03,执行后会生成test-report.html文件,该文件即HTML测试报告。效果如下所示: HTML测试报告01   未执行成功的测试用例,可点击查看详情,效果如下所示: HTML测试报告02   不同的测试结果在HTML测试报告中显示是不同的:

  • 通过:显示为绿色,不能点击详情查看。
  • 失败:显示为红色,能点击详情查看。
  • 阻塞:显示为黄色,能点击详情查看。
  • 超时:显示为灰色,能点击详情查看。
  • 未执行:显示为白色,不能点击详情查看。

其它

终止策略

  testauto支持3种终止策略:

  • ALL_COMPLETED:全部完成,默认。
  • FIRST_NOT_PASS:第一个未执行成功。
  • FIRST_P0_NOT_PASS:第一个P0测试用例未执行成功。

  终止策略使用StopStrategy枚举来设置:

if __name__ == '__main__':
    main(stop_strategy=StopStrategy.FIRST_NOT_PASS)

重试策略

  testauto支持3种重试策略:

  • NOT_RERUN:不重试,默认。
  • RERUN_NOW:立即重新执行测试用例,即:对当前未执行成功的测试用例,立即重新执行一次。
  • RERUN_LAST:最后重新执行测试用例,即:对全部未执行成功的测试用例,最后批量重新执行一次。

  重试策略使用RetryStrategy枚举来设置:

if __name__ == '__main__':
    main(retry_strategy=RetryStrategy.RERUN_NOW)

  注意终止策略的优先级是大于重试策略的,比如同时设置了FIRST_NOT_PASS和RERUN_NOW,当测试用例执行失败时,testauto会立即终止后续测试用例的执行,而不会对当前测试用例进行重新执行。

超时时间

  使用timeout参数可对单个测试用例设置超时时间:

if __name__ == '__main__':
    main(timeout=60)

  以上代码将单个测试用例的执行超时时间设置为了60秒,默认为1小时(3600秒)。

断言

  作为自动化测试框架,断言功能当然是不能少的,但testauto没有重复造轮子,而是直接使用Python自带的assert关键字来实现断言。比如TestCase11测试用例中断言的写法如下:

class TestCase11(TestCase):
    module = 'TestCase11模块'
    title = 'TestCase11标题'

    def test_case(self):
        sleep(2)
        assert False

  以上代码直接使用了assert关键字来断言。
  但testauto也新增了2个断言函数作为补充:

  • assert_raise():断言抛出指定异常。
  • assert_not_raise():断言不抛出指定异常。

  以下为演示代码,可参考这几个测试用例的写法来使用以上断言函数:

class TestCase13(TestCase):
    title = '抛出异常成功'

    def test_case(self):
        assert_raise(self._callable_target, RuntimeError)

    def _callable_target(self):
        raise RuntimeError('TestCase13的异常')


class TestCase14(TestCase):
    title = '不抛出异常成功'

    def test_case(self):
        assert_not_raise(self._callable_target, ValueError)

    def _callable_target(self):
        raise RuntimeError('TestCase14的异常')


class TestCase15(TestCase):
    title = '抛出异常失败'

    def test_case(self):
        assert_raise(self._callable_target, ValueError)

    def _callable_target(self):
        raise RuntimeError('TestCase15的异常')


class TestCase16(TestCase):
    title = '不抛出异常失败'

    def test_case(self):
        assert_not_raise(self._callable_target, RuntimeError)

    def _callable_target(self):
        raise RuntimeError('TestCase16的异常')

About

自动化测试框架,支持多线程和参数化测试,且自带HTML测试报告!

Resources

License

Stars

Watchers

Forks

Packages

No packages published