【Python】重写unittest用例执行器

最近在琢磨unittest的执行器,简单写个demo,实现多线程实现并发执行测试用例TestRunner,记录一下

import unittest

# unnittest 自带的执行器
suite = unittest.defaultTestLoader.discover(r"/****/testcases")

runner = unittest.TextTestRunner()
runner.run(suite)
# 不使用执行器执行用例
import unittest
from testcases.test_demo import TestDemo

result = unittest.TestResult()

case1 = TestDemo('test_login')

# 定义一个测试用例对象
res = case1.run(result)

case2 = TestDemo('test_register')

# 定义一个测试用例对象
case1.run(result)
case2.run(result)

print(res)
import unittest

# 内置执行器执行的原理
def test_runner(suite):
    result = unittest.TestResult()
    for item in suite:
        print(f"用例模块:{item}")
        for test_cls in item:
            print(f"测试类:{test_cls}")
            for case_ in test_cls:
                print(f"测试用例对象:{case_}")
                case_.run(result)
    return result


res = test_runner(suite)
print(res)

完善基于线程池的多线程的执行器

import unittest
from concurrent.futures import ThreadPoolExecutor


class TestRunner:

    def __init__(self, thread_count):
        self.thread_count = thread_count
        self.result = unittest.TestResult()

    def run(self, suite):
        """执行器执行用例的方法"""
        with ThreadPoolExecutor(max_workers=self.thread_count) as pool:

            for item in suite:
                print(f"用例模块:{item}")
                for test_cls in item:
                    print(f"测试类:{test_cls}")
                    # 遍历测试用例
                    # self.cls_run(test_cls)
                    pool.submit(self.__cls_run, test_cls)  # 以类为线程可以保障用例的执行顺序
        return self.result

    def __cls_run(self, test_cls):
        """执行测试类中所有的测试用例"""
        for case_ in test_cls:
            print(f"测试用例对象:{case_}")
            case_.run(self.result)


if __name__ == '__main__':
    suite = unittest.defaultTestLoader.discover(r"/Users/***/Yio/WrokHomePy/PWDemo/testcases/")
    runner = TestRunner(2)
    res = runner.run(suite)
    print(res)

模块级别的执行器

import unittest
from concurrent.futures import ThreadPoolExecutor


class TestRunner:

    def __init__(self, thread_count):
        self.thread_count = thread_count
        self.result = unittest.TestResult()

    def run(self, suite):
        """以模块为执行的执行器,这种可以保证模块内的测试用例的执行顺序"""
        with ThreadPoolExecutor(max_workers=self.thread_count) as pool:
            for item in suite:
                print(f"用例模块:{item}")
                pool.submit(item.run, self.result)
        return self.result


if __name__ == '__main__':
    suite = unittest.defaultTestLoader.discover(r"/Users/***/Yio/WrokHomePy/PWDemo/testcases/")
    print(f"收集到的测试用例数:{suite.countTestCases()}")
    runner = TestRunner(2)
    res = runner.run(suite)
    print(res)

用例收集套件的区别

import unittest

# 通过路径加载测试套件
suite = unittest.defaultTestLoader.discover(r"/Users/***/Yio/WrokHomePy/PWDemo/testcases")

# 通过测试模块去加载测试用例
from testcases import test_demo

# 模块级别测试套件:里面类基本的测试套件(测试类基本的套件中包含用例)
suite2 = unittest.defaultTestLoader.loadTestsFromModule(test_demo)

# 通过测试类去加载测试用例
from testcases.test_demo import TestDemo

# 测试类级别测试套件:里面包含的就是测试用例
suite3 = unittest.defaultTestLoader.loadTestsFromTestCase(TestDemo)

print()

重写TestResult,结合自定义的runner

import platform
import time
import unittest
from concurrent.futures import ThreadPoolExecutor


class TestResult(unittest.TestResult):
    """自定义TestResult"""

    def __init__(self, case_sum):
        super().__init__()
        # 收集到的用例总数
        self.case_sum = case_sum
        # 执行通过的用例
        self.success_cases = []
        # 用例执行开始时间
        self.test_start_time = None
        # 用例执行结束时间
        self.test_stop_time = None

    def startTestRun(self) -> None:
        """所有测试执行前"""
        print("当前系统版本:", platform.platform())
        print("python版本:", platform.python_version())
        print("套件中的用例总数:", self.case_sum)
        self.test_start_time = time.time()

    def stopTestRun(self) -> None:
        """所有测试用例执行完之后,执行的方法"""
        self.test_stop_time = time.time()
        print(f"执行用例数:{self.testsRun}")
        print(f"失败用例数:{len(self.failures)}")
        print(f"通过用例数:{len(self.success_cases)}")
        print(f"异常用例数:{len(self.errors)}")
        print(f"跳过用例数:{len(self.skipped)}")
        print(f"用例执行开始时间:{self.test_start_time},结束时间:{self.test_stop_time}, 总时间:",
              self.test_stop_time - self.test_start_time)

    def addSuccess(self, test):
        # 用例执行通过调用
        self.success_cases.append(test)
        print(f"用例{test}----> 执行通过")

    def addFailure(self, test, err):
        super().addFailure(test, err)
        print(f"用例{test}----> 执行失败")

    def addError(self, test, err):
        super().addError(test, err)
        print(f"用例{test}----> 执行报错:{err}")

    def addSkip(self, test: unittest.case.TestCase, reason: str):
        super().addSkip(test, reason)
        print(f"用例{test}----> 跳过:{reason}")


class TestRunner:

    def __init__(self, path, task, thread_count):
        self.suite = unittest.defaultTestLoader.discover(path)
        self.thread_count = thread_count
        self.result = TestResult(self.suite.countTestCases())
        self.task = task

    def __parse_suite_to_task(self):
        """解析测试套件,加入到执行的任务列表"""
        tasks = []
        if self.task == 'cls':
            """测试类级别的用例执行"""
            for item in self.suite:
                print(f"用例模块:{item}")
                for test_cls in item:
                    print(f"测试类:{test_cls}")
                    tasks.append(test_cls)
        elif self.task == 'model':
            """测试模块级别的用例执行"""
            for model in self.suite:
                print(f"用例模块:{model}")
                tasks.append(model)
        else:
            for model in self.suite:
                print(f"用例模块:{model}")
                for cls in model:
                    print(f"用例模块:{cls}")
                    for case_ in cls:
                        print(f"测试类:{case_}")
                        tasks.append(case_)
        return tasks

    def run(self):
        # 执行测试前,记录基本信息
        self.result.startTestRun()
        # 获取所有需要执行的任务
        tasks = self.__parse_suite_to_task()
        with ThreadPoolExecutor(max_workers=self.thread_count) as pool:
            """执行器线程池执行测试"""
            for work in tasks:
                pool.submit(work, self.result)
        # 所有测试结束后进行执行数据统计
        self.result.stopTestRun()
        # 然后这块可以生成一个html报告
        return self.result

    def __cls_run(self, test_cls):
        """执行测试类中所有的测试用例"""
        for case_ in test_cls:
            print(f"测试用例对象:{case_}")
            case_.run(self.result)


if __name__ == '__main__':
    runner = TestRunner(r"C:\PyWorkHome\demo\testcase", 'case', thread_count=2)
    result = runner.run()
    print(result)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值