【问题标题】:Integration testing: Start a blocking server during `unittest.setUp` before testing it?集成测试:在测试之前在“unittest.setUp”期间启动一个阻塞服务器?
【发布时间】:2011-01-24 10:18:01
【问题描述】:

我正在使用Thrift 编写服务,并且需要应用一些测试以确保它按预期运行/响应。为此,最可靠的方法似乎是使用unittest 模块。

我想直接在单元测试的setUp 方法中以“测试”模式启动服务(在特定的“测试”端口上启动,使用“测试”数据等),但调用serve()在那个时候阻塞等待连接。

启动服务以执行测试并使用tearDown 方法彻底关闭服务的最佳方法是什么?

【问题讨论】:

    标签: python unit-testing blocking thrift


    【解决方案1】:

    unittest 提供的“完全隔离”非常适合单元 测试(它的设计目的),但不一定适合集成测试——我看到了重用unittest 的吸引力,我自己这样做是为了利用我们周围的特殊测试运行器和c,但是,意识到使用unittest 来实现集成测试,我尝试通过编码而不是编写单元测试来进行补偿。

    当我必须在集成测试中生成服务器时,我倾向于在模块的开始处进行生成,在一个单独的过程中 -- 通过subprocess,或者通过其他适合您的安装的方式,如果你愿意在单独的节点或其他任何地方运行它——并在atexit 注册终止代码,当我的测试模块全部完成时,该终止代码将向该服务器发送终止请求。这不像 unit 测试所要求的那样“完全分离”,但我发现它对于集成测试来说已经足够好了,并且分摊了启动和初始化服务器的开销,这实际上可能非常高,跨越多个测试。

    即使您热衷于使用 setUp 和 tearDown,我仍然建议使用单独的进程(和单独的节点,如果您有很多人在“持续构建农场”等)。正如@Ned 的回答所暗示的那样,在同一进程中使用不同的线程让我觉得有风险——可能很容易在服务器和测试之间产生不必要的交互,隐藏一些错误或导致其他错误。

    如果我理解正确,您不仅要在同一个进程上运行服务器,而且还要在与测试相同的线程中运行服务器,这对我来说绝对是个坏主意——当然它会阻止一切,除非服务器的编码确实非常特别!-)

    【讨论】:

    • 这还是你2016年的做法吗?在我看来,webtest 和 mock 可以用来将套接字排除在外,但仍然可以得到他们想要的 OP。
    • @BrianTheLion,模拟不会为您提供集成测试——单元测试的关键区别值得牢记!
    • Mock 可以允许捕获作为字符串传出到套接字的请求数据。然后在同一个过程中使用该字符串来构造一个服务器端请求对象,该对象被馈送到 webtest.TestApp。服务器端的类似模拟允许捕获响应并将其返回给客户端,所有这些都无需 fork()ing。实现集成测试。 (请注意,这种方法不适用于流式传输场景。)
    • 在这里更新 RE:我上面的评论?
    【解决方案2】:

    您可以创建一个上下文管理器来在服务器在后台运行时启动您的测试。

    if __name__ == '__main__':
        with background_server():
            print('Server loaded, launching the tests...')
            unittest.main(exit=False)
    

    我用于background_server的代码:

    @contextlib.contextmanager
    def background_server():
        # Launching the server
        pid_server = os.fork()
        if not pid_server:  # Child code
            launch_server()  # Blocking call (until signal.SIGINT)
            print('Interuption detected, server closed...')
            sys.exit()  # Closing the process
    
        # HACK: Wait for the server to be launched
        while True:
            try:
                requests.get("http://localhost:5000/", timeout=0.5)
                break
            except requests.exceptions.ConnectionError:
                pass
            time.sleep(0.3)
    
        try:
            yield
        finally:
            # Closing the server
            os.kill(pid_server, signal.SIGINT)
    

    【讨论】:

      【解决方案3】:

      如果serve() 阻塞,那么最好的办法是在setUp 中生成一个线程来调用serve()。然后你必须弄清楚如何在tearDown方法中停止Thrift。

      【讨论】:

        猜你喜欢
        • 2014-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-10
        • 2018-01-14
        • 1970-01-01
        相关资源
        最近更新 更多