python3 unittest怎么做到上一个测试用例失败时自动跳过一下个测试用例

比如登录失败时,就跳过退出登录的用例,示例代码如下,大神求教,应该怎么写才能达到跳过的效果,如果使用@unittest.skipIf(),但是我怎么知道上个用例失败了呢

import unittest

class TestDemo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setupclass")

    def setUp(self):
        print("setup")

    def test_login(self):
        print("test_login")
        self.assertEqual(1, 2)  # 这里让登录判断为失败

    def test_logout(self):
        print("test_logout")

    def tearDown(self):
        print("teardown")

    @classmethod
    def tearDownClass(cls):
        print("teardownclass")

if __name__ == "__main__":
    testsuite = unittest.TestSuite()
    testsuite.addTest(TestDemo("test_login"))
    testsuite.addTest(TestDemo("test_logout"))
    runner = unittest.TextTestRunner()
    runner.run(testsuite)

我自己解决了哈哈哈,主要使用两个知识点,装饰器和 TestCase中的_outcome.result (python2中为_resultForDoCleanups),具体代码如下

import unittest
from functools import wraps


def skip_dependon(depend=""):
    """
    :param depend: 依赖的用例函数名,默认为空
    :return: wraper_func
    """
    def wraper_func(test_func):
        @wraps(test_func)  # @wraps:避免被装饰函数自身的信息丢失
        def inner_func(self):
            if depend == test_func.__name__:
                raise ValueError("{} cannot depend on itself".format(depend))
            # print("self._outcome", self._outcome.__dict__)
            # 此方法适用于python3.4 +
            # 如果是低版本的python3,请将self._outcome.result修改为self._outcomeForDoCleanups
            # 如果你是python2版本,请将self._outcome.result修改为self._resultForDoCleanups
            failures = str([fail[0] for fail in self._outcome.result.failures])
            errors = str([error[0] for error in self._outcome.result.errors])
            skipped = str([error[0] for error in self._outcome.result.skipped])
            flag = (depend in failures) or (depend in errors) or (depend in skipped)
            if failures.find(depend) != -1:
                # 输出结果 [<__main__.TestDemo testMethod=test_login>]
                # 如果依赖的用例名在failures中,则判定为失败,以下两种情况同理
                # find()方法:查找子字符串,若找到返回从0开始的下标值,若找不到返回 - 1
                test = unittest.skipIf(flag, "{} failed".format(depend))(test_func)
            elif errors.find(depend) != -1:
                test = unittest.skipIf(flag, "{} error".format(depend))(test_func)
            elif skipped.find(depend) != -1:
                test = unittest.skipIf(flag, "{} skipped".format(depend))(test_func)
            else:
                test = test_func
            return test(self)
        return inner_func
    return wraper_func


class TestDemo(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("setupclass")

    def setUp(self):
        print("setup")

    def test_login(self):
        print("test_login")
        self.assertEqual(1, 2)  # 这里让登录判断为失败

    @skip_dependon(depend="test_login")
    def test_logout(self):
        print("test_logout")
        self.assertEqual(1, 1)

    @skip_dependon(depend="test_logout")
    def test_1(self):
        print("test1")

    @skip_dependon(depend="test_1")
    def test_2(self):
        print("test2")

    def tearDown(self):
        print("teardown")

    @classmethod
    def tearDownClass(cls):
        print("teardownclass")

if __name__ == '__main__':
    testsuite = unittest.TestSuite()
    testsuite.addTest(TestDemo("test_login"))
    testsuite.addTest(TestDemo("test_logout"))
    testsuite.addTest(TestDemo("test_1"))
    testsuite.addTest(TestDemo("test_2"))
    runner = unittest.TextTestRunner()
    runner.run(testsuite)

coding=utf-8

import json
import re
import unittest
import requests
from inspect import isfunction
from functools import wraps

def skipIf(exp, reason):
"""自己实现unittest.skipIf 装饰器,不满足条件跳过测试用例,原因?装饰器是静态编译的,而unittest.skipIf在编译时就会判断condition是否满足条件,
如果我们想要动态决定是否跳过测试用例,就需要在执行测试用例的时候,在执行之前判断条件是否满足
:param exp: 依赖的表达式
:param reason: 跳过原因
:return: wraper_func
"""

def wraper_func(test_func):
    @wraps(test_func)  # @wraps:避免被装饰函数自身的信息丢失
    def func(self):
        if isfunction(exp):
            res = exp()
            if res == 1:
                self.skipTest(reason)
                return
            else:
                return test_func(self)
        else:
            raise TypeError('条件必须是函数')
            return
    return func
return wraper_func

status = 0

class Test(unittest.TestCase):
request = requests.Session()

def setUp(self):
    print('setup')
    # name = input("请输入name:")
    # print(name)

def tearDown(self):
    print('tearDown')

@classmethod
def setUpClass(cls):
    print('setUpClass')

@classmethod
def tearDownClass(cls):
    print('tearDownClass')

def test_True(self):
    self.assertTrue(True)

def test_login(self):
    headers = {
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "origin": "https://passport.mp-t.mallcoo.cn",
        "x-requested-with": "XMLHttpRequest"
    }
    data = {
        'username': '345345',
        'password': '34534',
        'ispersistent': 'true'
    }
    r = self.request.post(
        'https://passport.mp-t.mallcoo.cn/Home/PostLogin', data=data, headers=headers)
    res = r.json()
    global status
    if res["success"] == 1:  # 1 代表true, 0 代表false#
        print('登陆成功')
        status = 200
    else:
        print('登陆失败')
        status = 500
    self.assertTrue(res["success"], "登陆成功")

@skipIf(lambda: status == 500, "登录失败")
def test_getWeatherCitys(self):
    r = self.request.get(
        'http://bi.mp-t.mallcoo.cn/Project/GetWeatherCitys?_c=1567591439050')
    res = r.json()
    if res['dic'][0]['Code'] == 'beijing':
        try:
            assert res['sussess'] == 1  # 1 代表true, 0 代表false#
            print('断言通过')
        except AssertionError as e:
            print('断言不通过')
    else:
        print('464641830@qq.com' + "登录失败")

if name == '__main__':
suite = unittest.TestSuite()
tests = [Test("test_True"), Test("test_login"),
Test("test_getWeatherCitys")]
suite.addTests(tests)
with open('UnittestTextReport.txt', 'a', encoding='utf-8', errors='ignore') as f:
runner = unittest.TextTestRunner(stream=f, verbosity=2)
runner.run(suite)