zoukankan      html  css  js  c++  java
  • pytest文档35-Hooks函数之统计测试结果(pytest_terminal_summary)

    前言

    用例执行完成后,我们希望能获取到执行的结果,这样方便我们快速统计用例的执行情况。
    也可以把获取到的结果当成总结报告,发邮件的时候可以先统计测试结果,再加上html的报告。

    pytest_terminal_summary

    关于TerminalReporter类可以在_pytest.terminal中查看到

    from _pytest import terminal
    
    pytest_terminal_summary(terminalreporter, exitstatus, config)
    最后的结果汇总,可以拿到所有的执行结果
    参数:
    - terminalreporter (_pytest.terminal.TerminalReporter) – 内部使用的终端测试报告对象
    - exitstatus (int) – 返回给操作系统的返回码
    - config(_pytest.config.Config) - pytest config对象
    

    TerminalReporter部分代码

    class TerminalReporter(object):
        def __init__(self, config, file=None):
            import _pytest.config
    
            self.config = config
            self._numcollected = 0
            self._session = None
            self._showfspath = None
    
            self.stats = {}
            self.startdir = config.invocation_dir
    
    
        def report_collect(self, final=False):
            if self.config.option.verbose < 0:
                return
    
            if not final:
                # Only write "collecting" report every 0.5s.
                t = time.time()
                if (
                    self._collect_report_last_write is not None
                    and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION
                ):
                    return
                self._collect_report_last_write = t
    
            errors = len(self.stats.get("error", []))
            skipped = len(self.stats.get("skipped", []))
            deselected = len(self.stats.get("deselected", []))
            selected = self._numcollected - errors - skipped - deselected
            if final:
                line = "collected "
            else:
                line = "collecting "
            line += (
                str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s")
            )
            if errors:
                line += " / %d errors" % errors
            if deselected:
                line += " / %d deselected" % deselected
            if skipped:
                line += " / %d skipped" % skipped
            if self._numcollected > selected > 0:
                line += " / %d selected" % selected
            if self.isatty:
                self.rewrite(line, bold=True, erase=True)
                if final:
                    self.write("
    ")
            else:
                self.write_line(line)
    

    案例参考

    先在test_a.py写几个用例

    # test_a.py
    import pytest
    # 上海-悠悠
    
    def test_1():
        print("测试用例1111")
        assert 1 == 1
    
    
    @pytest.mark.skip("跳过")
    def test_2():
        print("测试用例22222")
        assert 1 == 1
    
    
    def test_3():
        print("测试用例3333")
    
    
    def test_4():
        print("测试用例44444444")
        assert 1 == 2
    

    test_b.py用例参考

    # test_b.py
    import time
    # 上海-悠悠
    
    def test_5():
        print("测试用例55555555")
        time.sleep(3)
    
    
    def test_6():
        print("测试用例66666666")
        time.sleep(3)
        assert 1 == 2
    

    于是在conftest.py中写个 pytest_terminal_summary 函数收集测试结果

    import time
    from _pytest import terminal
    # 上海-悠悠
    
    
    def pytest_terminal_summary(terminalreporter, exitstatus, config):
        '''收集测试结果'''
        print(terminalreporter.stats)
        print("total:", terminalreporter._numcollected)
        print('passed:', len(terminalreporter.stats.get('passed', [])))
        print('failed:', len(terminalreporter.stats.get('failed', [])))
        print('error:', len(terminalreporter.stats.get('error', [])))
        print('skipped:', len(terminalreporter.stats.get('skipped', [])))
        # terminalreporter._sessionstarttime 会话开始时间
        duration = time.time() - terminalreporter._sessionstarttime
        print('total times:', duration, 'seconds')
    
    

    运行结果

    D:softpytest_xuexi_demo>pytest
    ============================= test session starts =============================
    platform win32 -- Python 3.6.0, pytest-4.5.0, py-1.5.4, pluggy-0.13.1
    rootdir: D:softpytest_xuexi_demo
    plugins: allure-pytest-2.8.6, PyTestReport-0.1.9.3, forked-0.2, html-1.19.0, metadata-1.7.0, repeat-0.7.0, rerunfailures-8.0, xdist-1.23.2
    collected 6 items
    
    test_a.py .s.F                                                           [ 66%]
    test_b.py .F                                                             [100%]
    
    ================================== FAILURES ===================================
    ___________________________________ test_4 ____________________________________
    
        def test_4():
            print("测试用例44444444")
    >       assert 1==2
    E       assert 1 == 2
    
    test_a.py:21: AssertionError
    ---------------------------- Captured stdout call -----------------------------
    测试用例44444444
    ___________________________________ test_6 ____________________________________
    
        def test_6():
            print("测试用例66666666")
            time.sleep(3)
    >       assert 1 == 2
    E       assert 1 == 2
    
    test_b.py:18: AssertionError
    ---------------------------- Captured stdout call -----------------------------
    测试用例66666666
    {'': [<TestReport 'test_a.py::test_1' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_1' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_2' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_3' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_3' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_4' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_4' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_5' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_5' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_6' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='teardown' outcome='passed'>], 'passed': [<TestReport 'test_a.py::test_1' when='call' outcome='passed'>, <TestReport 'test_a.py::test_3' when='call' outcome='passed'>, <TestReport 'test_b.py::test_5' when='call' outcome='passed'>], 'skipped': [<TestReport 'test_a.py::test_2' when='setup' outcome='skipped'>], 'failed': [<TestReport 'test_a.py::test_4' when='call' outcome='failed'>, <TestReport 'test_b.py::test_6' when='call' outcome='failed'>]}
    total: 6
    passed: 3
    failed: 2
    error: 0
    skipped: 1
    total times: 6.150860786437988 seconds
    ================ 2 failed, 3 passed, 1 skipped in 6.15 seconds ================
    

    setup和teardown异常情况

    如果setup出现异常,test_b.py代码修改下

    # test_b.py
    import time
    import pytest
    # 上海-悠悠
    
    
    @pytest.fixture(scope="function")
    def setup_demo():
        raise TypeError("ERROR!")
    
    
    def test_5(setup_demo):
        print("测试用例55555555")
        time.sleep(3)
    
    
    def test_6():
        print("测试用例66666666")
        time.sleep(3)
        assert 1 == 2
    

    重新运行用例,结果如下

    total: 6
    passed: 2
    failed: 2
    error: 1
    skipped: 1
    成功率:33.33%
    total times: 3.1817877292633057 seconds
    =========== 2 failed, 2 passed, 1 skipped, 1 error in 3.18 seconds ============
    

    此时统计结果没什么问题,接下来看teardown异常情况

    # test_b.py
    import time
    import pytest
    # 上海-悠悠
    
    
    @pytest.fixture(scope="function")
    def setup_demo():
        yield 
        raise TypeError("ERROR!")
    
    
    def test_5(setup_demo):
        print("测试用例55555555")
        time.sleep(3)
    
    
    def test_6():
        print("测试用例66666666")
        time.sleep(3)
        assert 1 == 2
    

    运行结果

    {'': [<TestReport 'test_a.py::test_1' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_1' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_2' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_3' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_3' when='teardown' outcome='passed'>, <TestReport 'test_a.py::test_4' when='setup' outcome='passed'>, <TestReport 'test_a.py::test_4' when='teardown' outcome='passed'>, <TestReport 'test_b.py::test_5' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='setup' outcome='passed'>, <TestReport 'test_b.py::test_6' when='teardown' outcome='passed'>], 'passed': [<TestReport 'test_a.py::test_1' when='call' outcome='passed'>, <TestReport 'test_a.py::test_3' when='call' outcome='passed'>, <TestReport 'test_b.py::test_5' when='call' outcome='passed'>], 'skipped': [<TestReport 'test_a.py::test_2' when='setup' outcome='skipped'>], 'failed': [<TestReport 'test_a.py::test_4' when='call' outcome='failed'>, <TestReport 'test_b.py::test_6' when='call' outcome='failed'>], 'error': [<TestReport 'test_b.py::test_5' when='teardown' outcome='failed'>]}
    total: 6
    passed: 3
    failed: 2
    error: 1
    skipped: 1
    成功率:50.00%
    total times: 6.18759298324585 seconds
    =========== 2 failed, 3 passed, 1 skipped, 1 error in 6.19 seconds ============
    

    这个时候总用例是6,但是2 failed, 3 passed, 1 skipped, 1 error加起来的个数是7,这个为什么?

    从 terminalreporter.stats 可以看出 passed 里面 when='call' 时候统计了一次 test_5 用例

    <TestReport 'test_b.py::test_5' when='call' outcome='passed'>
    

    error 里面 when='teardown' 又统计了一次 test_5 用例

    'error': [<TestReport 'test_b.py::test_5' when='teardown' outcome='failed'>]
    

    when='teardown' 是测试用例的后置操作,一般用于数据的清理,报错了的话不影响测试用例的执行结果,所以可以忽略掉

    修改后的最终代码如下

    import time
    from _pytest import terminal
    # 上海-悠悠
    
    
    def pytest_terminal_summary(terminalreporter, exitstatus, config):
        '''收集测试结果'''
        # print(terminalreporter.stats)
        print("total:", terminalreporter._numcollected)
        print('passed:', len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown']))
        print('failed:', len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown']))
        print('error:', len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown']))
        print('skipped:', len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown']))
        print('成功率:%.2f' % (len(terminalreporter.stats.get('passed', []))/terminalreporter._numcollected*100)+'%')
    
        # terminalreporter._sessionstarttime 会话开始时间
        duration = time.time() - terminalreporter._sessionstarttime
        print('total times:', duration, 'seconds')
    

    运行后的结果

    total: 6
    passed: 3
    failed: 2
    error: 0
    skipped: 1
    成功率:50.00%
    total times: 6.20133113861084 seconds
    =========== 2 failed, 3 passed, 1 skipped, 1 error in 6.20 seconds ============
    
  • 相关阅读:
    opencv实现连通域
    C Tips:显示点阵汉字的小样例
    协方差的意义
    HashTable类模板_C++
    Java实现 蓝桥杯VIP 算法提高 选择排序
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
    Java实现 蓝桥杯VIP 算法提高 笨小猴
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/12741564.html
Copyright © 2011-2022 走看看