运行测试说明_选择并运行测试

这个由三部分组成的系列 文章第一篇文章介绍了python测试的变革,这要归功于zope.testingpy.testnose等标准测试框架。 这些支持更简单的测试习惯用法,并且可以代替项目传统上必须为运行测试而编写和维护的临时代码。 第二篇文章研究了这些自动化解决方案如何在Python包中进行搜索以识别可能包含测试的模块。

本文采取了下一步措施,并询问框架在对一个测试模块进行内部检查以发现其中包含哪些测试时会做什么。 它还着眼于细节,例如三个框架如何支持或不支持常见的测试设置和拆卸。

Zope框架中的测试发现

确定了感兴趣的模块列表后,如何发现其中的实际测试?

首先zope.testing框架,您会发现有关Zope社区的一些有趣信息。 他们倾向于构建更小型,更有限的,能够连接在一起的工具,而不是构建各自解决多个问题的大型工具。 zope.testing个例子, zope.testing模块实际上根本不提供检测测试的机制!

取而代之的是, zope.testing留给每个程序员,以便在每个模块中查找值得运行的测试,并将它们放到一个列表中。 它在每个测试模块中仅查找一件事:它调用一个test_suite()函数,期望返回标准unittest.TestSuite类的实例,该类填充了该模块定义的测试。

一些使用zope.testing程序员只是在test_suite()函数中手动创建和维护此测试列表。 其他人则编写自定义代码,这些代码带有一些捷径来发现已定义和可用的测试。 但是,最有趣的选择是使用另一个Zope软件包z3c.testsetup ,该软件包具有与其他现代Python测试框架一样自动发现软件包中各个测试的能力。

同样,这很好地说明了Zope程序员如何倾向于编写可用于构建框架而不是大型整体解决方案的构建块。 z3c.testsetup软件包既不包含可以选择测试的命令行界面,也没有包含可以显示测试结果的任何输出模块。 这些功能完全取决于zope.testing

实际上, z3c.testsetup用户通常甚至不使用zope.testing来发现测试模块。 相反,他们通过保持默认行为(即仅查找名为test.py模块)并在其整个源代码树中仅提供一个具有该名称的模块来保持默认状态,从而缩短zope.testing算法。 在最简单的情况下,他们的test.py看起来像这样:

import z3c.testsetup
test_suite = z3c.testsetup.register_all_tests(my_package)

这将测试发现的任务从zope.testing ,取而代之的是依靠z3c.testsetup本身为发现提供的更强大的机制。

可以为register_all_tests()函数提供几个配置选项。 有关详细信息,请参见z3c.testsetup文档,但此处仅概述其基本行为。 与本文讨论的所有其他框架不同, z3c.testsetup默认情况下并不关心包中每个Python模块的名称,而是关心其内容。 它将检查包中的所有模块以及所有.txt.rst文件,并选择在其文本中的某个位置指定:Test-Layer: 。 然后,它通过组合模块内部的所有TestCase类和文本文件内部的所有doctest节来构建测试套件。

使用:Test-Layer:字符串用测试标记文件是一种有趣的机制。 它确实具有缺点,即在浏览软件包的文件时,新程序员必须打开每个文件,或者至少打开grep的:Test-Layer:字符串,以查找测试的位置。 (更不用说z3c.testsetup显然必须做同样的事情;这是否使它比仅对文件名起作用的框架要慢?)

还要注意,最后,Zope的测试框架只支持或者是测试UnitTest实例或文档测试。 如本系列第一篇文章所述,更现代的Python测试框架也支持普通的Python函数作为有效测试。 这需要一种不同的测试检测算法,正如您现在将注意力转移到这些框架上所看到的。

py.test和鼻子中的测试发现

如前一篇文章所述, py.testnose框架使用相似但略有不同的规则集在Python软件包中搜索他们认为将包含测试的模块。 但是,两种情况都处于相同的情况:他们必须检查一系列模块,以找到开发人员要作为测试运行的功能和类。

如您在上一篇文章中py.testpy.test倾向于选择一个单一标准,所有使用该标准的项目都应遵循该标准,而nose允许以可预测的行为为代价进行更广泛的自定义。 在这种情况下是相同的:对于py.test ,在测试模块内部检测测试的规则是固定的,不变的和可预测的,而对于nose则可以灵活地定制。 如果项目使用nose进行测试,则必须首先访问该项目的setup.cfg文件,然后才能知道nose是否遵循其通常的检测测试规则,或者是否遵循该特定项目的特定规则。

以下是py.test使用的过程:

  • py.test在Python测试模块中查找时,它将收集名称以test_开头的每个函数以及名称以Test开头的每个类。 无论类是否继承自unittest.TestCase它都会收集类。
  • 测试函数只是运行,但是必须在测试类中搜索方法。 实例化该类后,所有名称以test_开头的方法都将作为测试运行。
  • 如果py.test框架提供了恰好继承自标准Python unittest.TestCase类的测试类,则它会显示出奇怪的行为:即使该类具有多个有吸引力的test_方法,如果py.test不具有这种吸引力, py.test也会死于异常还包含一个runTest()方法。 但是,如果确实的方法确实存在,那么py.test会忽略它; 它必须存在才能接受该类,但由于它不是以test_开头的,因此将不会运行。

    要解决此问题,请在项目的conttest.py文件中或使用-p命令行选项激活框架的unittest插件:

    $ py.test -p unittest

    这会使py.test对其行为进行三处更改。 首先,它不仅会检测名称以Test开头的测试类,还将检测从unittest.TestCase继承的任何其他类。 其次, py.test将不再报告不提供runTest()方法的TestCase子类的异常。 第三,也是最后, TestCase子类上的所有setUp()tearDown()方法将在该类包含的测试之前和之后以标准方式正确运行。

nose的行为,虽然更易于定制,但在这里变得更简单:

  • nose在Python测试模块中查找时,它将收集与选择测试模块时使用的正则表达式匹配的函数和类。 (默认情况下,该名称查找包含单词Testtest ,但是可以在命令行或配置文件中提供其他正则表达式。)
  • nose在测试类内部时,它将运行与该正则表达式匹配的方法。
  • 无需询问, nose将始终检测unittest.TestCase子类并将其用作测试。 但是,它将使用自己的正则表达式来确定哪些方法是测试,而不是使用^test的标准unittest模式。

生成测试

正如您在第一篇文章中所看到的, py.testnose都通过支持以简单函数编写的测试,使Python中的测试编写起来非常容易,例如:

# test_new.py - simple tests functions

def testTrue(self):
    assert True == 1

def testFalse(self):
assert False == 0

当您想要做的只是在某个特定的特定情况下检查组件的行为时,测试函数以及更传统的测试类就可以了。 但是,当您要进行一系列除某些参数外几乎相同的测试时该怎么办?

为了使这种情况易于实现,而不必剪切和粘贴测试功能的十几个副本,然后将名称更改为唯一, py.testnose支持生成测试 。 这里的想法是,您提供一个实际上是迭代器的测试函数,并使用其yield语句返回一系列函数以及要调用它们的参数。 例如,要针对您喜欢的每个Web浏览器运行一个测试,您可以编写如下内容:

# test_browser.py

def check(browser, page):
    t = TestBrowser(browser)
    t.load_page(page)
    t.check_status(200)

def test_browsers():
    for b in 'ie6', 'ie7', 'firefox', 'safari':
        for p in 'index.html', 'about.html':
        yield check, b, p

对于生成测试, py.test提供了另一项便利。 这样一来,您可以更轻松地判断测试是否分开进行,从而了解测试报告(如果其中一个或多个失败),则您产生的每个元组中的第一项可以是一个名称,该名称将作为考试:

# Alternate yield statement, for py.test
...
yield 'Page %s browser %s' % (b,p), check, b, p

生成测试应提供更具吸引力的解决方案比许多的比较尴尬的技术使用自制的测试,或限制自己什么是当前在许多项目中进行参数测试, unittest是有能力的。

设置和拆卸

在设计和编写测试套件时,一个巨大的问题是如何处理常见的设置和拆卸代码。 许多实际的测试与本文作为示例使用的非常简单的功能并不相似。 他们必须要做的事情,例如在Firefox中打开网页,单击标记为“继续”的按钮,然后检查结果。 在实际测试甚至还没有开始之前(我的意思是打开页面并点击按钮),测试必须首先完成几个昂贵的步骤。

现在,考虑一百个功能测试,它们全部执行这样的测试。 他们每个人都需要调用一个通用的设置例程来使Firefox运行,然后才能开始自己的特定测试。 结合以下事实:可能需要使用拆卸代码才能撤消设置,然后在测试套件中完成了200多个额外的函数调用。 其每个功能将如下所示:

# How test functions look if they each do setup and teardown

def test_index_click_continue():
    do_big_setup()          # <- the same in every test
    t = TestBrowser(browser)
    t.load_page('index.html')
    t.click('#continue')
    t.check_status(200)
    do_big_teardown()       # <- the same in every test

为了消除像这样的重复代码,许多测试框架提供了一种方法,用于指示一次要为整个测试组中的哪些运行设置和拆卸代码。

本文关注的所有三个框架zope.testingpy.testnose支持程序员编写的任何unittest.TestCase类的标准setUp()tearDown()例程。 但是,除此之外,这些框架在提供通用设置代码的功能上也有很大不同。

尽管zope.testing本身并没有提供额外的设置和拆卸支持,但是上面讨论的z3c.testsetup扩展在z3c.testsetup做了一些有趣的事情。 您会记得,它通过在:Test-Layer:某处查找带有:Test-Layer:文件来查找测试。 doctest中的图层实际上可以指定两个不同值之一。 将doctest标记为属于unit层意味着它无需任何特殊设置即可运行。 但是将其标记为属于functional层意味着它仅在调用框架设置函数后才能运行。

通常, :Test-Layer: functional测试旨在在Zope Web框架已完全配置后运行,以便它们可以创建测试浏览器实例,发送请求并查看Web框架作为响应返回。 通过愿意代表doctest执行此设置, z3c.testsetup可以节省大量的模板代码, z3c.testsetup其复制到每个功能性doctest中。

最后一个便利(这也减少了样板代码)是,可以为z3c.testsetup提供一个变量列表,以预加载到每个单元doctest的命名空间中,并为功能doctest预加载另一个变量。 这消除了将常见的import语句缠结粘贴到每个doctest文件顶部的需要。

转到py.test ,默认情况下它不支持设置和拆卸。 除非您已打开其unittest插件,否则它甚至不会运行标准unittest.TestCase类的setUp()tearDown()方法。

当涉及到支持通用测试代码时, nose真的发光了。 在发现测试时, nose与发现它们的上下文保持一致。 就像它认为unittest.TestCase子类中的每个测试方法都在该类“内部”并因此由其setUp()tearDown()方法控制一样,它也认为测试在其模块的“内部”(即封闭)包,以及该包之外的任何包。 因此,对于nose ,测试不在一个同心容器中进行,而是在一系列同心容器中进行,其中任何一个容器都可以包含在测试之前运行的设置代码和在测试之后运行的拆卸代码。

阅读nose文档以获取有关包装范围和模块范围的设置和拆卸功能的更多信息; 除其他细节外,您还将了解到对于设置和拆卸功能的调用有各种各样的选择。 (再次, nose似乎很难鼓励不同的项目以相同的方式编写测试,以便它们可以轻松地读取彼此的代码。)但是,它们是一种非常有效的方法,可以将功能分组到包和模块中,而不仅仅是结构化的(它们都放在这里了)而且还有语义(这里的测试都在同一环境中运行)。

在某些情况下, nose不关心设置和拆卸功能的名称:使用@with_setup装饰器为特定功能明确指定它们时。 同样,如果您对此感兴趣,请查阅nose文档。 在这里,我只想留意一下,由于函数是Python中的一类对象,因此您可以为特定的装饰器分配一个名称,并反复使用它:

# Naming a with_setup decorator

firefox_test = with_setup(firefox_setup, firefox_teardown)

@firefox_test
def test_index_click():
    ...

@firefox_test
def test_index_menu():
...

最后一个区别:而在指定的安装和拆卸功能@with_setup装饰或作为方法提供unittest.TestCase子类的get运行一次为每个函数或测试,他们包的安装和拆卸的代码,你给nose在向上模块或软件包级别对于整个测试集仅运行一次。 因此,不要期望这样的测试彼此正确隔离:它们将共享您在模块或程序包的设置例程中创建的任何资源的单个副本。

结论

恭喜你! 现在,您了解了不同的测试框架如何通过检测我们的测试并安排它们运行来支持我们(或无法支持我们)。 本系列的最后一篇文章将探讨框架用于收集测试的所有工作的回报:强大的测试选择选项,报告工具和调试支持,这些功能使测试结果对我们有用。 最后,我们将考虑如何从这三个框架中选择最适合您需求的框架。


翻译自: https://www.ibm.com/developerworks/aix/library/au-pythontesting3/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值