资讯专栏INFORMATION COLUMN

异步编程 101:如何测试 async 代码

sPeng / 3114人阅读

摘要:整个事件循环是在一个线程里面的,意味着任务切换更加高效,无需上下文转换。异步代码很高效,但是也有很蛋疼的地方,那就是测试。所以我们得想办法告诉使用一个来运行测试方法。

视频原文:Strategies for testing Async code - PyCon 2019

同时参考了:

Testing Asyncio Python Code with Pytest

前面几篇关于异步编程的文章:

异步编程 101: 是什么、小试Python asyncio

异步编程 101:Python async await发展简史

异步编程 101:写一个事件循环

异步编程 101:asyncio中的 for 循环

异步编程 101:asyncio 进阶上篇

异步编程,本质上是通过合作(cooperation)来达成并发效果,也即:需要 wait 的时候,也就是发生 IO 的时候,把控制权交给主事件循环。 (yield control when "awaiting" asynchronous results.) 这个过程有点像事件循环完成了操作系统的工作,可以将事件循环看作是操作系统,然后把协程看作是线程这么来理解。整个事件循环是在一个线程里面的,意味着任务切换更加高效,无需上下文转换。

异步代码很高效,但是也有很蛋疼的地方,那就是测试。

0x01 : async 测试实例

来通过一个简单的例子看一下吧:一个Cat类,有一个 move 方法,这个方法是异步的。

然后用 unittest 写一个测试类,你能发现下面代码的问题吗?

herd(grafield, "forward") 返回的是一个协程对象(coroutine object),如果你不await他,什么也不会发生。而coroutine object是 truthy 的,所以assertTrue() 是能够通过的。如果你运行一下 test,会看到coroutine herd was nerver awaited的 warning。

下图这样调用await还是不对的,因为await关键字只能出现在async函数里面。

一个解决方案是加入事件循环:

这能work,但是估计你也看出来了,这很麻烦。如果我有多个方法,难道我需要每个 test方法都加一个事件循环吗?更重要的是,我只是想做一下单元测试,事件循环在这个时候实际上是一个底层细节,我不需要关心。

在 Python3.7 中,asyncio新增了一个方法:asyncio.run(),为你隐藏了事件循环的细节,所以能够让代码更加简洁:

0x02 pytest-asyncio

安装:pip install pytest-asyncio,这实际上是pytest的一个插件。

用法很简单,重要的是我们得知道工作原理。之前的代码问题在于,pytest 默认的的 runner 会将所有的函数当作普通函数处理,而对于 async 函数, 调用的时候返回的是一个 coroutine object。所以我们得想办法告诉 pytest 使用一个 eventloop 来运行测试方法。

一种方法是,实例化一个eventloop 然后注入到 tests里面,比如:

import asyncio
import pytest

async def say(what, when):
    await asyncio.sleep(when)
    return what

@pytest.fixture
def event_loop():
    loop = asyncio.get_event_loop()
    yield loop
    loop.close()


def test_say(event_loop):
    expected = "This should fail!"
    assert expected == event_loop.run_until_complete(say("Hello!", 0))

这种方法不方便之处在于每次都需要手动注入 eventloop,更优雅的方法是调整 test runner,让它识别 async 函数,当作 asyncio tasks 来执行。

pytest-asyncio完成的功能就是这样的,它的 API 非常简单,你只需要为 async function 添加一个 @pytest.mark.asyncio 修饰器即可:

import pytest
from say import say

@pytest.mark.asyncio
async def test_say():
    assert "Hello!" == await say("Hello!", 0)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7001.html

相关文章

  • Python爬虫入门教程 7-100 蜂鸟网图片爬取之二

    摘要:蜂鸟网图片简介今天玩点新鲜的,使用一个新库,利用它提高咱爬虫的爬取速度。上下文不在提示,自行搜索相关资料即可创建一个对象,然后用该对象去打开网页。可以进行多项操作,比如等代码中等待网页数据返回创建线程,方法负责安排执行中的任务。 1. 蜂鸟网图片-简介 今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohtt...

    z2xy 评论0 收藏0
  • Python爬虫入门教程 7-100 蜂鸟网图片爬取之二

    摘要:蜂鸟网图片简介今天玩点新鲜的,使用一个新库,利用它提高咱爬虫的爬取速度。上下文不在提示,自行搜索相关资料即可创建一个对象,然后用该对象去打开网页。可以进行多项操作,比如等代码中等待网页数据返回创建线程,方法负责安排执行中的任务。 1. 蜂鸟网图片-简介 今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohtt...

    hufeng 评论0 收藏0
  • Python爬虫入门教程 7-100 蜂鸟网图片爬取之二

    摘要:蜂鸟网图片简介今天玩点新鲜的,使用一个新库,利用它提高咱爬虫的爬取速度。上下文不在提示,自行搜索相关资料即可创建一个对象,然后用该对象去打开网页。可以进行多项操作,比如等代码中等待网页数据返回创建线程,方法负责安排执行中的任务。 1. 蜂鸟网图片-简介 今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohtt...

    mcterry 评论0 收藏0
  • js-csp 异步编程的一些简单的例子

    摘要:发现一个月没刷技术文章了有点慌整理一篇短的用法出来只包含最基本的用法在里边最清晰不过我是在写的版本的实现包含异步用法会更繁琐一些但是也值得看看我相信普及之前还是一个很有意思的选择我的代码写的是可以自动脑补圆括号花括号上去注意包含的函数自动 发现一个月没刷技术文章了, 有点慌, 整理一篇短的 CSP 用法出来,只包含最基本的用法, 在 Go 里边最清晰, 不过我是在 Clojure 写的...

    curried 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<