zoukankan      html  css  js  c++  java
  • python 单元测试中处理用例失败的情况

    今天有一个需求, 在单元测试失败的时候打印一些日志, 我们管他叫 dosomething 吧 ,反正就是做一些操作
    查了下并没有查到相关的方法, 于是研究了一波unittest 的源码

    发现了这个东西

    try:
        self._outcome = outcome
    
        with outcome.testPartExecutor(self):
            self.setUp()
        if outcome.success:
            outcome.expecting_failure = expecting_failure
            with outcome.testPartExecutor(self, isTest=True):
                testMethod()
            outcome.expecting_failure = False
            with outcome.testPartExecutor(self):
                self.tearDown()
    
        self.doCleanups()
        for test, reason in outcome.skipped:
            self._addSkip(result, test, reason)
        self._feedErrorsToResult(result, outcome.errors)
        if outcome.success:
            if expecting_failure:
                if outcome.expectedFailure:
                    self._addExpectedFailure(result, outcome.expectedFailure)
                else:
                    self._addUnexpectedSuccess(result)
            else:
                result.addSuccess(self)
        return result
    finally:
        result.stopTest(self)
        if orig_result is None:
            stopTestRun = getattr(result, 'stopTestRun', None)
            if stopTestRun is not None:
                stopTestRun()
    
        # explicitly break reference cycles:
        # outcome.errors -> frame -> outcome -> outcome.errors
        # outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
        outcome.errors.clear()
        outcome.expectedFailure = None
    
        # clear the outcome, no more needed
        self._outcome = None
    

    其中重点关注下testMethod() 这个正是我们执行的用例
    于是去看了下testPartExecutor 用例失败的处理是在这里进行处理的

    def testPartExecutor(self, test_case, isTest=False):
        old_success = self.success
        self.success = True
        try:
            yield
        except KeyboardInterrupt:
            raise
        except SkipTest as e:
            self.success = False
            self.skipped.append((test_case, str(e)))
        except _ShouldStop:
            pass
        except:
            exc_info = sys.exc_info()
            if self.expecting_failure:
                self.expectedFailure = exc_info
            else:
                self.success = False
                self.errors.append((test_case, exc_info))
            # explicitly break a reference cycle:
            # exc_info -> frame -> exc_info
            exc_info = None
        else:
            if self.result_supports_subtests and self.success:
                self.errors.append((test_case, None))
        finally:
            self.success = self.success and old_success
    
    

    奈何, 他只是在self.errors (其中self为我们测试类的一个实例)中加了点东西

    于是对self.errors 进行观察,发现及时用例是正常的,他依然由内容.
    这..............于是我想到他最终是怎么打出来失败的log的
    看到 上面代码中的 self._feedErrorsToResult(result, outcome.errors)
    于是找到了这个东西

    def _feedErrorsToResult(self, result, errors):
        for test, exc_info in errors:
            if isinstance(test, _SubTest):
                result.addSubTest(test.test_case, test, exc_info)
            elif exc_info is not None:
                if issubclass(exc_info[0], self.failureException):
                    result.addFailure(test, exc_info)
                else:
                    result.addError(test, exc_info)
    

    从上面的代码我们可以知道 如果 error的第二项是None那么就是一个执行成功的用例,经过实验并确认了这个事情

    现在知道了 unittest 是如何处理 失败用例的了

    于是便有了下面这种方法

    def tearDown(self):
        errors = self._outcome.errors
        for test, exc_info in errors:
            if exc_info:
                # dosomething
                pass
    

    上面这种方法尽量少的改变原来的逻辑, 想到一种新的方法解决问题

    既然unittest没有处理这个事情,那我们魔改之

    于是有了下面这种方法

    注意: 不建议魔改代码

    import sys
    import contextlib
    import unittest
    from unittest.case import SkipTest, _ShouldStop, _Outcome
    
    
    @contextlib.contextmanager
    def testPartExecutor(self, test_case, isTest=False):
        old_success = self.success
        self.success = True
        try:
            yield
        except Exception:
            try:
                # if error
                getattr(test_case, test_case._testMethodName).__func__._error = True
                raise
            except KeyboardInterrupt:
                raise
            except SkipTest as e:
                self.success = False
                self.skipped.append((test_case, str(e)))
            except _ShouldStop:
                pass
            except:
                exc_info = sys.exc_info()
                if self.expecting_failure:
                    self.expectedFailure = exc_info
                else:
                    self.success = False
                    self.errors.append((test_case, exc_info))
                # explicitly break a reference cycle:
                # exc_info -> frame -> exc_info
                exc_info = None
        else:
            if self.result_supports_subtests and self.success:
                self.errors.append((test_case, None))
        finally:
            self.success = self.success and old_success
    
    
    _Outcome.testPartExecutor = testPartExecutor
    
    
    class MyTest(unittest.TestCase):
        def test_1(self):
            print("test_1")
    
        def test_2(self):
            print("test_2")
            raise ValueError
    
        def tearDown(self):
            if hasattr(getattr(self, self._testMethodName), "_error"):
                # dosomething
                pass
    
        # def tearDown(self):
        #     推荐这种方法
        #     errors = self._outcome.errors
        #     for test, exc_info in errors:
        #         if exc_info:
        #             # dosomething
        #             pass
    
    
    if __name__ == '__main__':
        unittest.main()
    
    

    这样我们就可以在用例执行失败后在tearDown的时候做一些操作

  • 相关阅读:
    004-ant design -dispatch、request、fetch
    003-and design-dva.js 知识导图-02-Reducer,Effect,Subscription,Router,dva配置,工具
    002-and design-dva.js 知识导图-01JavaScript 语言,React Component
    003-and design-在create-react-app项目中使用antd
    002-and design-基于dva的基本项目搭建
    001-ant design安装及快速入门【基于纯antd的基本项目搭建】
    103-advanced-上下文
    102-advanced-代码分割
    101-advanced-React易用性,概述
    007-spring cache-缓存实现-02-springboot ehcahe2、ehcache3实现、springboot caffeine实现
  • 原文地址:https://www.cnblogs.com/twotigers/p/10657345.html
Copyright © 2011-2022 走看看