Testing with asyncio

之前有说过应用开发者不需要将loop当作参数在函数间传递,只需要调用asyncio.get_event_loop()即可获得。但是在写单元测试时,可能会需要用多个loop(每个测试用一个单独的loop),问题来了:是否为了支持单元测试而要将loop作为函数参数传入呢?

先看个例子。

import asyncio
from typing import Callable

async def f(notify: Callable[[str], None]):    # 1
    # < ... some code ... >
    loop = asyncio.get_event_loop()    # 2
    loop.call_soon(notify, 'Alert!')    # 3
    # < ... some code ... >
  1. 想象一个coroutine内部需要通过call_soon调用另一个函数,这个函数可能是logging,发聊天信息,短线股票操作或其它任何操作;

  2. 仍然不通过函数参数来获取loop,但要记住一点,这个方法调用始终获取的是当前线程的loop;

  3. 将回调函数及其参数添加到loop的下一次迭代中。


最佳方式是通过fixture来为异步代码提供loop,Pytest将fixtures中定义的函数返回值作为参数传入测试函数中,描述起来有些复杂,用代码展示一下。

# conftest.py    # 1
import pytest

@pytest.fixture(scope='function')   # 2
def loop():
    loop = asyncio.new_event_loop()    # 3
    try:
        yield loop
    finally:
        loop.close()    # 在结束时关闭loop
  1. Pytest将会自动导入名称为“conftest.py”的文件并使其中的配置生效;

  2. 这里创建了一个fixture,scope参数告诉Pytest这个fixture的作用范围,用function限制将会使得每个函数都获得新的loop;

  3. 创建一个全新的loop,但不会立刻让其开始运行。


上述代码有个错误,不要直接使用它,错误很微妙,但也是本章的全部要点,下面开始讨论它,先给一个测试用例。

from somewhere import f    # 1

def test_f(loop):    # 2
    collection = []    # 3
    def f_notify(msg):    # 4
        collection.append(msg)

    loop.create_task(f(f_notify))   # 5
    loop.call_later(1, loop.stop)   # 6
    loop.run_forever()

    assert collection[0] == 'Alert!'    # 7
  1. 这里当作伪代码,表示f是一个在其它模块中定义的coroutine;

  2. Pytest会识别loop函数名并从fixtures中找到这个函数并传入它的调用返回值;

  3. 用一个容器收集notify的信息;

  4. 这是notify函数;

  5. 安排一个coroutine调用notify作为task;

  6. 因为loop是run_forever的,用call_later确保loop会停止;

  7. 此处进行测试。


上面提到有个错误,在这个导入的coroutine函数f中,loop是通过get_event_loop()获得的,而非fixture中提供的,所以这个测试会失败,因为通过get_event_loop()获得的loop压根不会运行。

一个解决办法就是明确地给函数传入loop作为参数,这样就能保证正确的loop被使用,然而这样写代码十分痛苦,因为这样一来大量的函数都要传入loop参数。

有个更好的办法就是,当一个新的loop运行时,将这个loop设置为当前线程的loop,这样get_event_loop()返回的总是最新的loop,这对单元测试十分有用。

# conftest.py
import pytest

@pytest.fixture(scope='function')
def loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)    # 这个方法执行后,所有后续的get_event_loop获得的都是fixture中的loop,不需要显式地将loop作为参数传入了
    try:
        yield loop
    finally:
        loop.close()    

深入Asyncio(十二)Asyncio与单元测试的更多相关文章

  1. springboot(十二):springboot单元测试、打包部署

    单元测试 1.在pom包中添加spring-boot-starter-test包引用 <dependency> <groupId>org.springframework.boo ...

  2. 20155322 2016-2017-2 《Java面向对象程序设计》第十二周课堂练习之Arrays和String单元测试

    20155322 2016-2017-2 <Java面向对象程序设计>第十二周课堂练习之Arrays和String单元测试 练习目地 在IDEA中以TDD的方式对String类和Array ...

  3. Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)

    前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...

  4. python 并发专题(十三):asyncio (二) 协程中的多任务

    . 本文目录# 协程中的并发 协程中的嵌套 协程中的状态 gather与wait . 协程中的并发# 协程的并发,和线程一样.举个例子来说,就好像 一个人同时吃三个馒头,咬了第一个馒头一口,就得等这口 ...

  5. Spring Boot(十二):spring boot如何测试打包部署

    Spring Boot(十二):spring boot如何测试打包部署 一.开发阶段 1,单元测试 在开发阶段的时候最重要的是单元测试了,springboot对单元测试的支持已经很完善了. (1)在p ...

  6. 20155207JAVA第十二周课堂练习

    20155207JAVA第十二周课堂练习 教材代码检查--P98 修改教材P98 Score2.java, 让执行结果数组填充是自己的学号 Arrays和String单元测试 在IDEA中以TDD的方 ...

  7. 前端开发中SEO的十二条总结

    一. 合理使用title, description, keywords二. 合理使用h1 - h6, h1标签的权重很高, 注意使用频率三. 列表代码使用ul, 重要文字使用strong标签四. 图片 ...

  8. CRL快速开发框架系列教程十二(MongoDB支持)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  9. 我的MYSQL学习心得(十二) 触发器

    我的MYSQL学习心得(十二) 触发器 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数 ...

  10. Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】

    2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...

随机推荐

  1. 【Android】SQLite基本用法(转)

    在Android开发中SQLite起着很重要的作用,网上SQLite的教程有很多很多,不过那些教程大多数都讲得不是很全面.本人总结了一些SQLite的常用的方法,借着论坛的大赛,跟大家分享分享的.一. ...

  2. hdu 3579(中国剩余定理+考虑0)

    Hello Kiki Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  3. 删除svn控制

    1.用cmd 进去所要删除的目录 2.运行  for /r ./ %a in (./) do @if exist "%a/.svn" rd /s /q "%a/.svn& ...

  4. (21)python lambda表达式

    lambda表达式是一个匿名函数 通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用 最简单的例子 add = lambda x,y : x + y print add(3,5) #out ...

  5. 《HelloGitHub》第 38 期

    兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...

  6. 10.1综合强化刷题 Day2 morning

    一道图论神题(god) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有一张无向图G={V,E},这张无向图有n个点m条边组成.并且这是一张带权图,只有 ...

  7. eclipse启动Tomcat时报错:严重: Exception loading sessions from persistent storage

    我的项目工程是Spring+hibernate+structs  1.0,最近启动tomcat时多次遇到如下异常: 严重: IOException while loading persisted se ...

  8. Ubuntu 16.04下使用Wine安装Notepad++

    说明: 1.使用的Wine版本是深度出品(Deepin),已经精简了很多没用的配置,使启动能非常快,占用资源小. 2.关于没有.wine文件夹的解决方法:在命令行上运行winecfg: 下载: (链接 ...

  9. GCD和信号量

    GCD 概念不多说,直接上代码.话说也不是什么高深的东东,不过极大简化了代码,一目了然.后面对信号量的记录也采用了相同的原理. //抛出线程 dispatch_async(dispatch_get_g ...

  10. 判断vps类型