跳转到主要内容

支持灵活 harness 组成的测试支持代码

项目描述

使用大型框架的应用程序通常需要在提供对API所公开的资源的一致视图的方式上模拟或存根API的部分。测试固定件通常希望在比单个API调用更高级别上为API建立特定状态;如果API提供了多种执行方式,这种情况就更加如此。对测试代码如何使用底层API的兴趣不如API暴露的信息以及在其上执行的操作。

有几种方法可以使用基于 unittest.TestCase 固定件的测试来处理这些情况。但往往这些测试变得混乱,测试作者必须关注基类和混合类的实现细节,以避免不同API之间的支持相互干扰各自的内部状态。

这个库通过允许支持测试控制其他框架或库的API成为独立组件来解决这个问题。这些 固定组件 可以

  • 访问测试对象,

  • 参与设置和清理,

  • 提供清理处理程序,

  • 提供API以配置它们管理的API的行为,以及

  • 提供针对它们所做的事情的特定断言方法。

这些组件可以以适当的方式将其自身注入它们管理的API中(例如使用 mock.patch)。

发布历史

3.1.2 (2018-12-19)

小调整

  • 当没有准备好的响应时,由 kt.testing.requests 测试夹具引发的 AssertionError 现在为 PATCH、POST 和 PUT 请求提供了更实质性的消息,显示了更多有关有效载荷的信息。这可以使调试问题更容易。

3.1.1 (2018-10-19)

错误已修复

  • 打包:轮子不是通用的,因为我们不希望在 Python 3 下依赖 mock

3.1.0 (2018-10-19)

新功能

  • kt.testing.cleanup 支持在每次测试之前和之后(当使用 kt.testing.TestCase 时)全局注册清理函数。这是直接受 zope.testing.cleanup 的启发,并且与之兼容。

开发支持

  • 添加了 tox 配置以运行测试。

3.0.0 (2017-11-30)

向后不兼容的更改

  • kt 命名空间包现在切换到使用 pkgutil-风格构造,完全移除了 pkg_resources 支持。这应该不会影响许多用户。

    有关命名空间包样式的更多信息,请参阅 打包命名空间包

2.2.0 (2017-09-29)

新功能

  • 只要 decode_unicode 为 false,kt.testing.requests 响应对象就支持 iter_content 方法。

2.1.0 (2017-09-05)

新功能

  • kt.testing.requests 在较低级别拦截了 requests API,连接到底层的 requests.sessions.Session 对象而不是 requests.api.requests。这使得它能够与自己的会话对象或派生会话对象一起使用,只要没有重写 request 方法。

2.0.0 (2017-06-19)

新功能

  • 支持 Python 3。

1.2.0 (2016-09-20)

新功能

  • kt.testing.requests.RequestInfo 对象封装了 requests 从应用程序接收到的信息。这取代了存储在夹具组件 kt.testing.requests.Requestsrequests 属性中的 5 元组,并为提供的数据的部分提供了命名访问,以便在测试中具有更好的可读性。

1.1.0 (2016-05-10)

新功能

  • kt.testing.requests.Requests 方法 add_erroradd_response 增加了一个新的、可选的参数,filter,它接受一个与 requests.request 具有相同签名的可调用对象。结果是布尔值,表示请求是否应被视为与响应匹配。如果方法匹配且 URL 匹配,则只会调用过滤器函数。

    这可以用来检查请求体是否匹配某些期望。这对于 RPC 类型的接口(例如 XML-RPC 或 SOAP)特别有价值,在这些接口中,多个行为映射到相同的 URL 和 HTTP 方法。

  • 新增 kt.testing.requests.Requests 方法:add_connect_timeoutadd_read_timeoutadd_unreachable_host,以向配置的响应集中添加相应的异常。

1.0.0 (2016-03-21)

此库的初始公共版本最初是为 Keeper Technology 的内部使用创建的。

实现夹具组件

夹具组件由一个工厂对象定义,通常是类,并期望为夹具提供精简的 API。让我们看看一个简单但完整、可用的示例

import logging


class TestLoggingHandler(logging.StreamHandler):

    def __init__(self, stream, records):
        self.records = records
        super(TestLoggingHandler, self).__init__(stream)

    def handle(self, record):
        self.records.append(record)
        super(TestLoggingHandler, self).handle(record)


class LoggingFixture(object):

    def __init__(self, test, name=None):
        self.test = test
        self.name = name

    def setup(self):
        sio = cStringIO.StringIO()
        self.output = sio.getvalue
        self.records = []
        handler = TestLoggingHandler(sio, self.records)
        logger = logging.getLogger(self.name)
        logger.addHandler(handler)
        self.test.addCleanup(logger.removeHandler, handler)

从测试夹具中使用它很简单

import kt.testing


class TestMyThing(kt.testing.TestCase):

    logging = kt.testing.compose(LoggingFixture)

    def test_some_logging(self):
        logging.getLogger('my.package').error('not happy')

        record = self.logging.records[-1]

        self.assertEqual(record.getMessage(), 'not happy')
        self.assertEqual(record.levelname, 'ERROR')

夹具组件还可以提供一个无参数的 teardown 方法(除了 self)。这些在调用测试用例的 tearDown 方法之后调用,并且不需要该方法成功。(它们作为测试用例的清理函数调用。)

可以为固定组件提供构造函数参数,使用 kt.testing.compose,但请注意,测试用例实例始终作为第一个位置参数传递

class TestMyThing(kt.testing.TestCase):

    logging = kt.testing.compose(LoggingFixture, name='my.package')

    def test_some_logging(self):
        logging.getLogger('your.package').error('not happy')

        with self.assertRaises(IndexError):
            self.logging.records[-1]

每个测试用例类的实例都将获得它自己的固定组件实例,通过使用 kt.testing.compose 定义的属性访问。这些实例在调用测试用例的 __init__ 方法时就已经可用。

如果测试类重写了 setUp 方法,它需要确保调用超类的 setUp,以便调用固定组件的 setup 方法。

class TestSomeThing(kt.testing.TestCase):

    logging = kt.testing.compose(LoggingFixture, name='my.package')

    def setUp(self):
        super(TestSomeThing, self).setUp()
        # more stuff here

请注意,setUp 并没有直接调用 unittest.TestCase.setUp。由于 kt.testing.compose 可能会添加额外的混合类,因此使用 super 是正确的方法,除非你正在专门使用已知已正确混合了正确混合类的基类。

多个固定组件和测试继承

可以为单个测试类添加相同或不同类型的多个固定组件

class TestMyThing(kt.testing.TestCase):

    my = kt.testing.compose(LoggingFixture, name='my.package')
    your = kt.testing.compose(LoggingFixture, name='your.package')

    def test_different(self):
        self.assertIsNot(self.my, self.your)

使用固定组件的基类将得到正确初始化,属性可以以有意义的方式进行别名和重写

class TestAnotherThing(TestMyThing):

    orig_my = TestMyThing.my
    my = kt.testing.compose(LoggingFixture, name='my.another')

    def test_different(self):
        self.assertIsNot(self.my, self.your)
        self.assertIsNot(self.orig_my, self.your)
        self.assertIsNot(self.orig_my, self.my)

        self.assertEqual(self.my.name, 'my.another')
        self.assertEqual(self.orig_my.name, 'my.package')
        self.assertEqual(self.your.name, 'your.package')

kt.testing.requests - 对 requests 的干预

许多应用程序(和其他库)使用 requests 包来检索由 URL 标识的资源。通常直接使用 mock 来处理测试中对资源的请求是合理的,但有时需要更多。 requests 库提供了多种触发特定请求的方式,应用程序通常不需要关心使用哪种方式来发送请求。

提供了一个用于 requests 的固定组件

class TestMyApplication(kt.testing.TestCase):

    requests = kt.testing.compose(kt.testing.requests.Requests)

可以通过通过 compose 传递的构造函数参数提供默认响应实体。可以提供主体和内容类型。

class TestMyApplication(kt.testing.TestCase):

    requests = kt.testing.compose(
        kt.testing.requests.Requests,
        body='{"success": true, "value": "let's have some json data"}',
        content_type='application/json',
    )

如果没有定义默认响应实体,则使用类型为 text/plain 的空主体。

该固定组件提供这些方法,通过 URL 配置特定请求的响应

add_response(method, url, status=200, body=None, headers={}, filter=None)

为给定的 URL 和请求方法提供特定的响应。不会考虑请求的其他方面来识别提供哪种响应。

如果响应状态指示允许在响应中包含实体,并且 body 作为 None 提供,将返回默认主体和内容类型。除非为固定组件构造函数提供其他值,否则这将是一个空字符串。如果状态指示不应返回实体,则使用空主体。

如果 filter 提供,并且不是 None,它必须是一个可调用对象,接受与 requests.request 相同的签名,并返回一个布尔值,指示响应是否适用于正在进行的请求。如果结果是 true,则响应被视为匹配并将被消耗。如果为 false,则不会使用该响应,但会考虑后续请求。

提供的信息将用于创建通过 requests API 返回的响应。

add_error(method, url, exception, filter=None)

提供在请求特定资源时应引发的异常。这可以用来模拟错误,例如无响应的服务器或 DNS 解析失败。只有 URL 和请求方法被考虑来识别提供哪种响应。

add_connect_timeout(method, url, filter=None)

提供一个与主机在合理时间内未能连接时相同的异常结构。这使用 add_error,但节省了你自己构造异常的需要。

add_read_timeout(method, url, filter=None)

提供与主机连接但未在合理时间内响应相同的异常结构。这使用了 add_error,但无需自己构建异常。

add_unreachable_host(method, url, filter=None)

提供与主机不可达相同的异常结构。这使用了 add_error,但无需自己构建异常。

如果请求与任何提供的响应都不匹配,将引发一个 AssertionError;这通常会导致测试失败,除非被测试的代码过于积极地捕获异常。

如果没有消耗所有配置的响应就完成了测试,将在拆卸过程中引发一个 AssertionError。基于 unittest 的测试运行器通常将此报告为错误而不是失败,但这将需要开发人员进行检查,这正是目的所在。

如果为相同的请求方法和方法(无论是响应还是错误)配置了多个配置,它们将按照配置的顺序提供给应用程序。

kt.testing.cleanup - 全局清理注册

许多库和应用程序最终都会维护一些小的全局状态。这些部分可能是缓存,或者是从配置中推导出来的信息,但它们需要在测试之间清除,以避免测试以难以调试的方式相互干扰。

在测试的 setUptearDown 方法中清除这些模块状态的部分可以解决这个问题,但每个应用程序都需要了解库和应用程序中存在的每个这样的模块状态部分;这可能是一个挑战。

允许每个库或模块注册一个清理函数,这使得能够收集确保测试清理足够所需的一切。

这种做法是在 zope.testing 包的 cleanup 模块中构建的,该模块提供了注册和调用清理函数的函数。 kt.testing.cleanup 模块提供了一个类似的API。如果也使用了 zope.testing.cleanupkt.testing.cleanup 将通过共享背后的清理函数注册来合作。

有两个函数提供了 kt.testing.cleanup API

register(func, *args, **kwargs)

注册一个可调用的函数,该函数应在清理模块状态时调用。可调用的函数将使用提供的附加位置和关键字参数调用。

func 应该快速简单,并且不得引发异常。

cleanup()

调用所有注册的清理。清理函数将按注册顺序调用。如果也使用了 zope.testing.cleanup,则通过每个API注册的清理可能根据注册顺序混合。

kt.testing.TestCasesetUptearDown 方法都调用 cleanup 函数。

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分发

kt.testing-3.1.2.tar.gz (23.3 kB 查看哈希值)

上传时间 源代码

构建的分发

kt.testing-3.1.2-py3-none-any.whl (19.0 kB 查看哈希值)

上传时间 Python 3

kt.testing-3.1.2-py2-none-any.whl (19.0 kB 查看哈希值)

上传时间 Python 2

由以下支持