最近公司越来越多的项目开始推动单元测试,而我在公司里很早就在进行单元测试实践。就用这篇文章作为一次内部技术分享的主题,同时也代表我自己对单元测试的认识和实践。
单元测试的概念
单元测试是软件测试的一种类型,测试对象是最基础的代码单元(函数、类、模块),属于白盒测试。在经典的测试金字塔中,单元测试处于最底层。

最简单的单元测试:

最近公司越来越多的项目开始推动单元测试,而我在公司里很早就在进行单元测试实践。就用这篇文章作为一次内部技术分享的主题,同时也代表我自己对单元测试的认识和实践。
单元测试是软件测试的一种类型,测试对象是最基础的代码单元(函数、类、模块),属于白盒测试。在经典的测试金字塔中,单元测试处于最底层。

最简单的单元测试:

之前在工作中遇到一个需求,需要在手机小程序端获取到微信小商店店铺的所有商品数据。由于当时我们没有在后台维护用户的商品数据,选择的解决方案是现场调用商品列表接口,然后缓存在 Redis 里。
鉴于 Redis 的内存还是比较宝贵的,而用户的商品数据(转化为 json 格式后)又是一些比较有规律的文本数据,比较适合进行数据压缩,于是我调研了一下 Python 中的数据压缩的方案。
zip标准库Python 中有一个标准库zip,常用来处理 zip 格式文件,提供的函数和方法也更适合文件操作。在这个案例中我们的数据是通过 http 接口获取的,额外进行一些文件操作有些麻烦和多余,zip标准库并不适合这个场景。
zlib标准库zlib是一个常用的压缩、解压库,使用了 deflate 算法。zip和unzip工具背后其实用的就是zlib。Python 内置了zlib标准库,提供的接口也很简洁易用(zlib.compress和zlib.decompress)。
1 | import zlib |
今天在公司处理了一个线上问题,涉及到在 Python 中处理语义化版本(Semantic Versioning),值得作为一个主题记录一下。
Node.js 的作者 Ryan Dahl 在 2018 年 JSConf EU 上发布了一篇演讲:10 Things I Regret About Node.js,讲述了他认为自己在设计 Node.js 时的十个(实际上演讲中只提到七个)失误,包括:没有坚持使用 Promise,使用了 GYP 构建系统,package.json 和 node_modules 的设计失误等。
在演讲中 Ryan Dahl 宣布了一个新项目:Deno,一个新的服务端 Javascript 运行时。经过两年多的发展,Deno 已经发布了 1.8 版本,也有了一个活跃的开发者社区。我们今天就来简单的了解一下 Deno。
Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.
Deno 使用 V8 引擎,由 Rust 构建,是一个简单、现代、安全的 Javascript 和 Typescript 运行时。
Deno 的主要特性有:
默认安全。外部代码没有文件系统、网络、环境的访问权限,除非显式开启。
支持开箱即用的 TypeScript 的环境。
只分发一个独立的可执行文件 (deno)。
有着内建的工具箱,比如一个依赖信息查看器 (deno info) 和一个代码格式化工具 (deno fmt)。
有一组经过审计的标准模块,保证能在 Deno 上工作。
脚本代码能被打包为一个单独的 JavaScript 文件。
Python 3.10 版本还在开发之中,目前释出的 dev 版本实现了新语法特性 Structural Pattern Matching(PEP 634):可以利用match语句和case语句匹配对象的不同模式,并应用不同的行为。
我先前自己尝试体验了一下Structural Pattern Matching语法(使用pyenv安装dev版本 Python 3.10),感觉很好用的,并且有很大的发挥空间。
Structural pattern matching has been added in the form of a match statement and case statements of patterns with associated actions. Patterns consist of sequences, mappings, primitive data types as well as class instances. Pattern matching enables programs to extract information from complex data types, branch on the structure of data, and apply specific actions based on different forms of data.
1 | match subject: |
我们可以使用match case直接匹配字面量。
1 | def http_error(status): |
最近使用 FastAPI 框架开发了一个 WEB 服务。
为了充分利用 FastAPI 作为一个 ASGI 框架的原生异步支持特性,很多业务代码也改成了异步函数,并且使用了异步的 HTTP 库httpx和 MongoDB 的异步 Python drivermotor。
由此带来的一个问题就是异步 Python 代码的单元测试的编写问题。
Python 的异步函数返回的是一个协程对象(coroutine),需要在前面加await才能获取异步函数的返回值,而只有在异步函数中才能使用await语句,这也意味着一般异步函数的测试代码本身也需要是一个异步函数。
1 | async def add(a:int, b:int): |
与 Javascript 不同,Python 的异步代码需要显示地运行在事件循环中。
Python3.7 以上的版本中可以直接调用asyncio.run函。
如果使用的是更早的 Python 版本,就需要指定一个事件循环对象来运行异步代码。
1 | import asyncio |
最近创建了几个 Python 的开源项目(u-coreutils、auto-logger、fastapi-redis-session)。
我在这些项目中开始使用poetry工具进行依赖管理,体验还是相当不错的。
poetry 简介Poetry是一个基于pyproject.toml的工具,利用poetry可以更加方便地进行 Python 项目的开发、构建、发布和依赖管理。
接下来通过一个例子来简单介绍一下 poetry 的使用。
我们将创建一个库greet,提供一个greet函数来输出”Hello, World”,代码和文件结构都很简单。
1 | # project/greet/__init__.py |
在project目录下运行poetry init(可以通过pip install -U poetry进行poetry工具的安装),会有很多提示信息用于确认项目的名称、版本、作者、协议、Python 版本,也可以在init过程中指定项目的依赖和dev依赖。
poetry init运行成功后,solution目录下会生成一个pyproject.toml文件。
可以手动设置一些项目的其他信息,例如仓库地址或者README文件的位置。
对数据进行排序是一个很常见的需求,但有时候我们并不需要对完整的数据进行排序,只需要排前几的数据,也就是经典的 Top-K 问题。
Top-K 问题的经典解法有两种:一种是脱胎于快速排序(Quick Sort)的快速选择(Quick Select)算法,核心思路是在每一次Partion操作后下一次递归只操作前K项数据。另一种是基于堆排序的方法。
Python 中有两个标准库可以原生的支持堆排序(优先队列),分别是heapq和PriorityQueue(queue)。
heapqheapq标准库提供了一些工具函数用来对list对象实现二叉堆的各种操作(就地修改list对象)。简单的用法如下:
1 | import heapq |
1 | assert heapq.heappop(arr) == 0 |