Python接口自动化学习—unittest框架文档学习总结

uinittest单元测试框架是受到JUnit的启发,unittest支持测试自动化,共享测试的设置和关闭代码,将测试聚合到集合中以及测试与报告框架的独立性。

  • test fixture

fixture表示测试之前的准备工作和清除工作。例如,创建临时或代理数据库,目录或启动服务器进程。

  • test case

test case 是测试的各个单元。它检查对一组特定输入的特定响应,unittest提供了一个基类,TestCase用来创建testcase。

  • test suite

test suite是test case的合集,通常用test suite将test case汇总然后一起执行。

  • test runner

test runner是一个执行器,它可以执行case 并提供结果给用户。可以提供图形界面、文本界面或者返回值表示测试结果。

举例说明
unittest模块提供了丰富的工具来构建和运行测试,本节演示了工具的一小部分足以满足大多数用户的需求。

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

通过子类创建测试用例unittest.TestCase。执行测试的方法命名需要test开头。
每个testcase都是用assertEqual()来检查预期结果。assertTrue、assertFalse、assertRaise验证是否引发了特定的异常。使用这些方法代替assert语句,测试运行程序可以累积所以测试结果并生成测试报告。
setUp()和tearDown允许你定义将在每次测试方法前后执行的指令,
运行结果:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

将-v选项传递给测试脚本将指示unittest.main() 启用更高级别的详细程度

test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

1 命令行界面

unittest支持以下命令行选项:

  • -b, buffer

在测试运行期间,将缓冲标准输出和标准错误流,通过测试期间的输出将被丢弃,在测试失败或错误时通常会回显输出添加到失败消息中。

  • -c, catch

在测试运行期间执行conrol -C ,等待当前测试结束,然后报告到目前为止的所有结果。再次按control -C引发正常KeyboardInterrupt异常。

  • -c, failfast

在第一个错误或失败时停止测试运行。

  • -k

仅运行与模式或字符串匹配的测试方法和类。此选项可以多次使用,在这种情况下,将包括与给定模式匹配的所有测试用例。

  • –locals

在回溯中显示局部变量。
新的3.2版本:命令行选项-b,-c并-f添加。
3.5版的新功能:命令行选项–locals。
3.7版的新功能:命令行选项-k。
命令行还可以用于测试发现,运行项目中的所有测试或仅运行子集。

2 Test Discovery

3.2版本的新功能
unittest支持简单的测试发现,为了兼容test discovery,所有测试文件必须是可从项目顶层目录导入模块或包(包括名称空间包)
Test Discovery在TestLoader.discover()中实现,但是仍能用于命令行模式。用法:

cd project_directory
python -m unittest discover

注意:如果要传递参数Test Discovery,则必须显示使用全命令。python -m unittestpython -m unittest discoverdiscover

该discover子命令具有以下选项:

-v, --verbose
详细输出

-s, --start-directory directory
开始发现的目录(.默认)

-p, --pattern pattern
模式以匹配测试文件(test*.py默认)

-t, --top-level-directory directory
项目的顶层目录(默认为起始目录)

的-s,-p和-t选项可以作为的顺序位置参数传递。以下两个命令行是等效的:

python -m unittest discover -s project_directory -p “_test.py"
python -m unittest discover project_directory "
_test.py”

除了作为路径外,还可以传递软件包名称,例如 myproject.subpackage.test,作为起始目录。然后,讲提供您提供的软件包名称,并将其在文件系统上的位置用作起始目录。

注意:Test Discovery通过导入来加载测试,一旦Test Discovery从开始目录中找到所有测试文件,就可以指定将路径转换为要导入的程序包名称。例如,foo/bar/baz.py将导入为foo.bar.baz。

如果在公共的包并且使用test discovery去加载该包的另一个不同副本,那么就会引发异常,导致discovery退出。
如果提供一个起始目录作为包名称而不是提供一个路径作为目录,那么discovery就会认为所有位置的文件都是你需要导入的(包括中间路径),这样就不会报错。

测试模块和包通过load_tests协议以指定的方式加载测试和运行discover。

1.4 组织测试代码

单元测试的基本构建模块是test cases,单一的测试逻辑场景并检查其正确性。在unittest中,test case被unittest.TestCase的实例表示。要制作自己的测试用例,必须编写TestCase的子类或者使用FunctionTestCase.

TestCase实例的测试代码必须是完整且是自包含的,来使他可以独立运行或任意数量的其他测试用例任意组合运行。
最简单的TestCase子类将简单实现一个测试方法(名称以开头的方法test),执行特定的代码:

import unittest

class DefaultWidgetSizeTestCase(unittest.TestCase):
    def test_default_widget_size(self):
        widget = Widget('The widget')
        self.assertEqual(widget.size(), (50, 50))

注意:为了测试某些内容,我们使用基类assert()的方法。如果测试失败,就会抛出一个异常并且显示具体信息,unittest就认为该测试失败。异常都会被认为失败。*

测试比较多的时候,测试的初始化会显得重复冗余复杂,我们可以将这部分单独拿出来,实现setUp()的方法,测试框架会自动的在每个测试执行前执行该方法。

import unittest

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def test_default_widget_size(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

    def test_widget_resize(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')

注意:通过根据字符串的内置顺序对测试方法名称进行排序,确定测试的运行顺序。

如果在setUp()测试运行时该方法引发异常,则框架将认为该测试已发生错误,并且该测试方法将不会执行。
同样我们可以提供一种tearDown()在测试方法运行后进行整理的方法。

import unittest

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def tearDown(self):
        self.widget.dispose()
    

如果setUp()成功,测试方法不管是成功或失败,tearDown()都会运行。
对于测试代码来讲,这样的工作机制称为Test Fixture。一个新的TestCase实例被当做 Test Fixture创建,用于执行每个测试方法。也就是说,TestCase.setUp、TestCase.tearDown和TestCase.__init__会在每个用例执行运行一次。

建议使用TestCase去根据测试的特性对其进行分组。unittest提供一个一个机制:测试套件(test suit),由unittest的TestSuite类实现。如果测试很多的话,调用unittest.main方法可以手机模型中所有的测试方法并执行。
自定义套件:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('test_default_widget_size'))
    suite.addTest(WidgetTestCase('test_widget_resize'))
    return suite
 
if __name__ == '__main__':
    runner = unittest.TextTestRunner()

可以将测试用例或者测试套件写在被测试的代码中,建议将测试部分单独写在一个文件,优势如下:
1)测试模块可以单独在命令行运行;
2)测试代码部分可以更容易的单独拿出来;
3)不会为了迎合被测试代码而随意修改测试代码;
4)测试代码修改少;
5)被测试代码容易被重构。
6)如果测试策略改变了,不需要去修改源码。

1.5 使用旧的测试代码

使用者会发现现存的一些旧的测试代码并不是TestCase的子类,但是仍需要执行。
基于此,unittest提供了FunctionTestCase类。TestCase的子类可以直接包含这些旧的测试类,并且同样可以实现用例的setUp()初始化和tearDown()销毁操作。

def testSomething():
    something = makeSomething()
    assert something.name is not None
    # ...

此时可以创建1个等效的测试用例,包含set-up和tear-down方法。

testcase = unittest.FunctionTestCase(testSomething,
                                     setUp=makeSomethingDB,
                                     tearDown=deleteSomethingDB)

注意:FunctionTestCase可以很快的将普通测试方法转换成单元测试,但一般不建议。尽量花时间去构造TestCase的子类作为测试将会使以后重构更加容易。

有时候,已有的测试可能使用该doctest模块编写。如果是这样,请doctest提供一个DocTestSuite类,该类可以unittest.TestSuite根据现有doctest基于现有的测试自动构建实例。

1.6 跳过测试和预期的失败

3.1版本的新功能
unittest支持跳过单个测试方法甚至整个测试类别。此外,它支持将测试标记为“预期失败”,即已损坏且将失败的测试,但是这个失败不会在TestResult中记录。
跳过一个测试很简单,只需使用skip()或者附带一些条件判断的skip()。
代码如下;

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

输出结果:

test_format (__main__.MyTestCase) ... skipped 'not supported in this library version'
test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping'
test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows'
 
----------------------------------------------------------------------
Ran 3 tests in 0.005s
 
OK (skipped=3)

类的跳过方法:

@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
    def test_not_run(self):
        pass

TestCase.setUp()也可以设置跳过测试,这在将一些资源设置为不可用时非常实用。

对于预期失败使用exceptedFailture()装饰器:

class ExpectedFailureTestCase(unittest.TestCase):
    @unittest.expectedFailure
    def test_fail(self):
        self.assertEqual(1, 0, "broken")

也可以自定义一个跳过装饰器,通过在测试上设置一个skip()的装饰器。这个装饰器会直接跳过该测试。

def skipUnlessHasattr(obj, attr):
    if hasattr(obj, attr):
        return lambda func: func
    return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))

以下装饰器实现了跳过测试以及预期失败的功能:

@unittest.skip(reason)
直接跳过被装饰的测试,reason是描述跳过的原因。

@unittest.skipif(condition,reason)
如果if的条件成立则跳过测试。

@unittest.expectedFailure
标记测试为失败,如果在运行时失败,则不会在结果中统计。

exception unittest.SkipTest(reason)
跳过测试抛出的异常。

可以使用TestCase.skipTest()或者上述装饰器中的某一个来替代这种抛异常的做法。如果测试被跳过,则不会执行setUp()和tearDown()方法;如果类被跳过,则不会执行setUpClass()和tearDownClass()方法;如果模块被跳过,则不会执行setUpModule()和tearDownModule()方法。

1.7 使用subTest()

版本3.4新增。
部分测试有细微差别,比如参数,unittest支持直接在测试方法内部使用subTest()上下文管理器来实现:

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):
                self.assertEqual(i % 2, 0)

输出结果


======================================================================
FAIL: test_even (__main__.NumbersTest) (i=1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 32, in test_even
    self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
 
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=3)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 32, in test_even
    self.assertEqual(i % 2, 0)
AssertionError: 1 != 0
 
======================================================================
FAIL: test_even (__main__.NumbersTest) (i=5)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 32, in test_even
    self.assertEqual(i % 2, 0)

如果代码中不使用subTest(),测试会在第一次失败就退出,并且错误信息不是很明白,不使用输出结果如下;

======================================================================
FAIL: test_even (__main__.NumbersTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 32, in test_even
    self.assertEqual(i % 2, 0)
AssertionError: 1 != 0

1.8 类和方法

1.8.1 Test Cases

unittest.TestCase(methodName=’ runTest’)类
在unittest系统中,TestCase的实例就是逻辑测试单元。该类作为一个基类,它的特性和功能通过创建子类实现。类内部实现了一些接口,可以让test runner驱动测试,以及测试中可以使用一些方法来断言和报告各种失败情况。
每一个TestCase的实例运行时都是运行一个方法:该方法名就是methodName。在大多数情况下,你都不需要修改methodName,也不需要重新实现runTest()方法。

版本3.2修改内容:TestCase可以不需要methodName就进行实例化,这使得TestCase在交互式平台上更容易使用。TestCase的实例的方法可以分为3种:一种是用来执行测试的;一种是实现条件断言和失败报告的,一种是用来收集测试本身的一些信息。

第一种方法大致有:
setUp()
该方法负责Test Fixture中的准备工作,一般在测试执行前调用;除 AssertionError和SkipTest以外,该方法触发的其他异常均做错误处理而不是测试失败,该方法默认没有操作。

tearDown()
该方法在测试方法被执行并且结果记录后立即被调用。如果测试方法出现异常,子类实现的功能就需要特别的检查。任何异常,除了AssertionError和SkipTest,只要是该方法抛出的异常都会被视为其他错误而不是测试失败(会在报告中统计)。该方法只有在setUp()执行成功时才会调用,给测试结果无关。默认该方法没有操作。

setUpClass()
当类中的测试方法被执行前会被调用的一个类方法。该方法只会在类方法前调用,也就是带有classmethod装饰器并且没有其他参数的方法。


@classmethod
def setUpClass(cls):
	...

版本3.2中新增

tearDownClass()
当类测试方法被执行完后会被调用的一个类方法。该方法只会在一个类的所有方法执行完成后调用,该方法被调用时,必须有classmethod装饰器并且除了类以外没有其他参数的方法。

@classmethod
def tearDownClass(cls):

版本3.2新增。

run(result=None)
执行测试,并收集信息作为结果传递给TestResult对象。如果结果为空,一个临时的报告文件就会被调用的defaultTestResult()方法创建并使用。再把结果对象返回。

通过简单的调用TestCase实例可以达到相同的效果。
版本3.3修改:早期的run版本中不会返回测试结果,也不会调用实例。

SkipTest(reason)
当需要跳过一个测试方法时,可以在测试方法或setUp()中调用该方法。
版本3.1新增。

**subTest(msg=None,params)
把一个封闭的代码块当做subTest执行并返回上下文管理器。msg和params是可选的,主要用来显示信息,以方便测试执行异常时定位问题。
一个测试中可以包含任意个subTest,并且可以多层嵌套。
版本3.4中新增。

debug()
运行测试但不生成报告。这样运行测试时会直接抛出异常,可以在调试模式下使用。
本TestCase类提供了一些断言方法来检查并报告故障。下表列出了最常用的断言方法
Python接口自动化学习—unittest框架文档学习总结_第1张图片
所有断言方法都接受一个msg的参数,用来在测试失败时显示。注意,msg关键字参数可以传递给assertRaises(), assertRaisesRegex(),assertWarns(),assertWarnsRegex() 只有当它们被用作上下文管理器。

assertEqual(第一,第二,msg=None)
测试第一和第二是否相等,不相等就失败。
如果first和second是完全相同的类型,并且将调用list,tuple,set,fronzenset或str或子类addTypeEqualityFunc()在特定于类型的相等性函数中注册的任何类型中一个,以便生成更有用的默认错误消息。
在版本3.1中进行了更爱:添加了类型特定的相等函数的自动调用。
在3.2版本中进行了更改:新增assertMultiLineEqual()方法作为比较字符串时调用的特定类型方法。

assertNotEqual(第一,第二,msg = None )
测试第一和第二个不相等。如果这些值确实比较相等,则测试将失败。

assertTrue(expr,msg = None )
assertFalse(expr,msg = None )

测试expr为true(或false)。

请注意,这等效于bool(expr) is True而不是等效expr is True (或者assertIs(expr, True))。当有更具体的方法可用时(例如assertEqual(a, b)代替assertTrue(a == b)),也应避免使用此方法 ,因为在发生故障的情况下它们可以提供更好的错误消息。

详细参考此文档
https://docs.python.org/3/library/unittest.html

如有侵权,请联系删除。。。

你可能感兴趣的:(python接口测试,unittest)