Python自动化框架UnitTest原理及应用之TestCase

unittest框架是受Junit启发而出现的单元测试框架,是目前企业中的主流测试框架

UnitTest基础:

1. 类对象必须继承于unittest.TestCase类

2.四大组件:

test case:测试用例,方法命名基于test_开头,测试用例自行排序执行,排序规则A-Z,a-z,0-9

test fixture:设置前置条件(setup),后置条件(teardown),每个测试用例方法执行前后都要执行这两个方法

test suite:测试套件,批量执行用例集,套件运行需要结合运行器(htmltestrunner/texttestrunner)

test runner:运行器,通过runner的run方法去调用执行测试用例集

3.运行机制:通过在main方法调用unittest.main()方法来执行用例case,也可以通过TestRunner.run执行TestSuite中的cases

下面我们来看下API文档

UnitTest API:

class unittest.TestCase(methodName='runTest')

Instances of the TestCase class represent the logical test units in the unittest universe. This class is intended to be used as a base class, with specific tests being implemented by concrete subclasses. This class implements the interface needed by the test runner to allow it to drive the tests, and methods that the test code can use to check for and report various kinds of failure.

Each instance of TestCase will run a single base method: the method named methodName. In most uses of TestCase, you will neither change the methodName nor reimplement the default runTest() method.

在 3.2 版更改: TestCase can be instantiated successfully without providing a methodName. This makes it easier to experiment with TestCase from the interactive interpreter.

每个创建的测试类都要继承unittest.TestCase父类,每个子类都将运行父类方法,被命名为methodName,实例化都时候不提供methodName也能成功。下面我们看下实例化的方法:

    def __init__(self, methodName='runTest'):
        """Create an instance of the class that will use the named test
           method when executed. Raises a ValueError if the instance does
           not have a method with the specified name.
        """
        self._testMethodName = methodName
        self._outcome = None
        self._testMethodDoc = 'No test'
        try:
            testMethod = getattr(self, methodName)
        except AttributeError:
            if methodName != 'runTest':
                # we allow instantiation with no explicit method name
                # but not an *incorrect* or missing method name
                raise ValueError("no such test method in %s: %s" %
                      (self.__class__, methodName))
        else:
            self._testMethodDoc = testMethod.__doc__
        self._cleanups = []
        self._subtest = None

        # Map types to custom assertEqual functions that will compare
        # instances of said type in more detail to generate a more useful
        # error message.
        self._type_equality_funcs = {}
        self.addTypeEqualityFunc(dict, 'assertDictEqual')
        self.addTypeEqualityFunc(list, 'assertListEqual')
        self.addTypeEqualityFunc(tuple, 'assertTupleEqual')
        self.addTypeEqualityFunc(set, 'assertSetEqual')
        self.addTypeEqualityFunc(frozenset, 'assertSetEqual')
        self.addTypeEqualityFunc(str, 'assertMultiLineEqual')

创建实例执行test方法,methodName参数默认为'runTest',跑用例的时候传入自己创建的测试类和方法就可以了,比如下面这样

class Test1(unittest.TestCase):
    def setUp(self) -> None:...
    def tearDown(self) -> None:...
    def test_test1(self):
        self.assertEqual('aaa','aaa')

suite = unittest.TestSuite()
suite.addTest(Test1('test_test1'))  #传入测试类和方法名
with open('测试报告.html', 'w', encoding='utf-8') as file:
    runner = HTMLTestRunner.HTMLTestRunner(stream=file, title='测试报告', description='冒烟日报')
    runner.run(suite)

这里需要注意下,方法名不能够随意写,必须已test_开头,否则创建实例的时候就会报'no such test method'的ValueError

当然如果我们不想这样命名,其实也能改,看下源码其实就是TestLoader的类属性testMethodPrefix

class TestLoader(object):
    """
    This class is responsible for loading tests according to various criteria
    and returning them wrapped in a TestSuite
    """
    testMethodPrefix = 'test'
    sortTestMethodsUsing = staticmethod(util.three_way_cmp)
    testNamePatterns = None
    suiteClass = suite.TestSuite
    _top_level_dir = None

通过改这个类属性,我们就可以随心所欲的命名了!好了,我们继续看API

TestCase instances provide three groups of methods: one group used to run the test, another used by the test implementation to check conditions and report failures, and some inquiry methods allowing information about the test itself to be gathered.

TestCase给我们提供了三组方法,1.测试执行方法。2.检查条件判断成功失败方法。3.能够收集信息的查询方法。

Methods in the first group (running the test) are:

setUp() -- Method called to prepare the test fixture.测试环境初始化,在每个测试用例执行前都会先被执行,需要覆写方法,因为父类中方法是空的

tearDown() -- Method called immediately after the test method has been called and the result recorded.测试环境去初始化,在每个测试用例执行后都会被执行,需要覆写方法 

setUpClass() -- 类方法,在测试类执行前调用一次,需要覆写方法

tearDownClass() -- 类方法,在测试类执行后调用一次,需要覆写方法

run(result=None) -- 收集TestResult对象传递的结果,如果结果被忽略或者为None,将通过defaultTestResult()创建临时结果返回给调用者

skipTest(reason) -- 跳过当前测试用例,使用形式为装饰器,还支持有条件的跳过,如skipif,skipunless等

class MyTestCase(unittest.TestCase):

    @unittest.skip("demonstrating skipping")
    def test_nothing(self):
        self.fail("shouldn't happen")

    @unittest.skipIf(mylib.__version__ < (1, 3),
                     "not supported in this library version")
    def test_format(self):
        # Tests that work for only a certain version of the library.
        pass

    @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
    def test_windows_support(self):
        # windows specific testing code
        pass

subTest(msg=None**params) -- 作为子测试方法执行一段封闭代码段,返回上下文管理器,比如:

class NumbersTest(unittest.TestCase):

    def test_even(self):
        """
        Test that numbers between 0 and 5 are all even.
        """
        for i in range(0, 6):
            with self.subTest(i=i):   #分成6个测试用例执行,这样相互独立
                self.assertEqual(i % 2, 0)

debug() -- 调试模式

The second group:检查条件判断成功失败方法,各种断言方法

Method

Checks that

New in

assertEqual(a, b)

a == b

 

assertNotEqual(a, b)

a != b

 

assertTrue(x)

bool(x) is True

 

assertFalse(x)

bool(x) is False

 

assertIs(a, b)

a is b

3.1

assertIsNot(a, b)

a is not b

3.1

assertIsNone(x)

x is None

3.1

assertIsNotNone(x)

x is not None

3.1

assertIn(a, b)

a in b

3.1

assertNotIn(a, b)

a not in b

3.1

assertIsInstance(a, b)

isinstance(a, b)

3.2

assertNotIsInstance(a, b)

not isinstance(a, b)

3.2

再来组Equal家族

Method

Checks that

New in

assertAlmostEqual(a, b)

round(a-b, 7) == 0

 

assertNotAlmostEqual(a, b)

round(a-b, 7) != 0

 

assertGreater(a, b)

a > b

3.1

assertGreaterEqual(a, b)

a >= b

3.1

assertLess(a, b)

a < b

3.1

assertLessEqual(a, b)

a <= b

3.1

assertRegex(s, r)

r.search(s)

3.1

assertNotRegex(s, r)

not r.search(s)

3.2

assertCountEqual(a, b)

a and b have the same elements in the same number, regardless of their order.

3.2

名字很美好,根据方法的名字,我们就能大概知道这些方法的用途。断言方法的入参也基本一致,基本都是(exprmsg=None)或(arg1,arg2,msg=None),除了上面的assertMethod外还有一些未列出来的,使用频率较低些,具体请看官方文档吧:https://docs.python.org/zh-cn/3.7/library/unittest.html#assert-methods

The third group:能够收集信息的查询方法,扩展功能方法和属性

fail(msg=None) -- 触发失败,可以传入相关信息

failureException -- 类属性,初始化值为AssertionError

longMessage -- 类属性,默认值为True,此时出现fail,自定义信息会被追加到标准信息后面输出,如果改为False,则只输出自定义信息

maxDiff -- 类属性,最长的fail报告输出,默认值为80*8字节,如果想不设长度限制,值设为None即可

还有一些是测试框架收集测试信息调用的一些方法

countTestCases() -- 返回TestCase数量,对于TestCase实例来说,总是返回1。不是特别理解,看了下源码也确实是直接return1了

defaultTestResult() -- 返回一个TestResult对象

等等

TestCase就简单写到这了,如有不妥还请各位大佬指正,一起交流学习,感谢🙏

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页