使用 Python 进行测试(7)...until you make it

news2025/2/7 23:50:12

在这里插入图片描述

总结

我很懒,我想用最少的行动实现目标,例如生成测试数据。我们可以:

使用随机种子保证数据的一致性。

>>> random.seed(769876987698698)
>>> [random.randint(0, 10) for _ in range(10)]
[10, 9, 1, 9, 10, 6, 5, 10, 1, 9]
>>> random.seed(769876987698698)
>>> [random.randint(0, 10) for _ in range(10)]
[10, 9, 1, 9, 10, 6, 5, 10, 1, 9]

使用mimesis生成虚假数据:

>>> from mimesis import Generic
>>> g = Generic()
>>> for _ in range(5):
...     print(
...         f"[{g.datetime.date()}] {g.person.full_name()}, "
...         f"PIN attempt {g.code.pin()} at "
...         f"{g.finance.bank()} ({g.address.city()}) "
...         f"from {g.internet.ip_v4()}"
...     )
[2005-08-20] Rosario Howe, PIN attempt 1155 at Bank of Marin (Solon) from 171.243.230.180
[2001-11-14] Julieann Spencer, PIN attempt 4044 at State Street Corporation (Round Rock) from 155.236.115.163
[2003-07-24] Mikel Buchanan, PIN attempt 1057 at Union Bank (Scarsdale) from 211.192.167.83
[2010-10-30] Antione Duffy, PIN attempt 6219 at Sandy Spring Bancorp (Phoenix) from 207.104.0.29
[2004-06-05] Damon Stephens, PIN attempt 6115 at First National Community Bank (Newport) from 226.230.244.209

使用freezegun冻结时间:

>>> import datetime
... from freezegun import freeze_time
...
... def calendar_with_cheesy_quote():
...     print(f"{datetime.date.today():%B %d, %Y}")
...     print("Each day provide it's own gift")
...
... with freeze_time("2012-01-14"):
...     calendar_with_cheesy_quote()

使用pyfakefs模拟内存中的文件系统:

from pyfakefs.fake_filesystem_unittest import Patcher
from pathlib import Path
import os

with Patcher() as patcher:
    fs = patcher.fs
    temp_path = Path(os.path.join(os.sep, 'tmp', 'tempdir'))
    fs.create_dir(temp_path)

    (temp_path / 'subdir1').mkdir()
    (temp_path / 'subdir2').mkdir()

    (temp_path / 'file1.txt').write_text('This is file 1')
    (temp_path / 'file2.txt').write_text('This is file 2')

    print(f'Now you see me: {temp_path}')
    print(f'Contents: {list(temp_path.iterdir())}')

    fs.remove_object(temp_path)

print(f"Now you don't: {temp_path.exists()}"")

通过VCR.py 记录HTTP调用:

import pytest
import requests

@pytest.mark.vcr
def test_single():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

使用inline-snapshot快照测试结果:

from inline_snapshot import snapshot

def test_something():
    assert 1548 * 18489 == snapshot() # snapshot() is the placeholder

现在是自由活动时间,去操场玩吧!

Less is more。

你可以手动创建伪造的数据,但很麻烦。让我们升级到自动化。

关于seed

许多生成数据的工具都使用seed产生伪随机。
实际上,真正的随机无法通过软件实现,只能通过现实中的随机源实现。但很多时候我们只需要一个无法被别人预测的值。
secrets使用随机性的操作系统源,random使用随机种子产生伪随机。

使用相同的随机种子,会得到同一个值:

>>> random.seed(769876987698698)
>>> [random.randint(0, 10) for _ in range(10)]
[10, 9, 1, 9, 10, 6, 5, 10, 1, 9]
>>> random.seed(769876987698698)
>>> [random.randint(0, 10) for _ in range(10)]
[10, 9, 1, 9, 10, 6, 5, 10, 1, 9]

一般虚假数据

从生成 HTML 填充到填充数据库,伪造数据是一项资产,可用于概念验证、文档,当然还有测试。

memesis 是一个极好的 Python 库,您可以 pip 安装,它可以让您生成成千上万的虚假电子邮件、地址、社会安全号码、颜色、食品和 lorem ipsums。

>>> from mimesis import Generic
>>> g = Generic()
>>> for _ in range(5):
...     print(
...         f"[{g.datetime.date()}] {g.person.full_name()}, "
...         f"PIN attempt {g.code.pin()} at "
...         f"{g.finance.bank()} ({g.address.city()}) "
...         f"from {g.internet.ip_v4()}"
...     )
[2005-08-20] Rosario Howe, PIN attempt 1155 at Bank of Marin (Solon) from 171.243.230.180
[2001-11-14] Julieann Spencer, PIN attempt 4044 at State Street Corporation (Round Rock) from 155.236.115.163
[2003-07-24] Mikel Buchanan, PIN attempt 1057 at Union Bank (Scarsdale) from 211.192.167.83
[2010-10-30] Antione Duffy, PIN attempt 6219 at Sandy Spring Bancorp (Phoenix) from 207.104.0.29
[2004-06-05] Damon Stephens, PIN attempt 6115 at First National Community Bank (Newport) from 226.230.244.209

您甚至可以设置区域设置。例如,如果我想要一些法语数据:

>>> from mimesis.locales import Locale
... g = Generic(locale=Locale.FR)
... for _ in range(5):
...     print(
...         f"[{g.datetime.date()}] {g.person.full_name()}, "
...         f"essai de PIN {g.code.pin()} à "
...         f"{g.finance.bank()} ({g.address.city()}) "
...         f"depuis {g.internet.ip_v4()}"
...     )
...
[2004-02-14] Joana Menard, tentative de PIN 4616 à Neuflize OBC (Colmar) depuis 193.125.166.248
[2012-07-08] Juliet Dastous, tentative de PIN 1168 à Banque de Savoie (Agen) depuis 6.232.176.31
[2018-01-23] Chloé Maurin, tentative de PIN 4101 à LCL (Menton) depuis 142.144.98.158
[2021-03-18] Nabil Jutras, tentative de PIN 6478 à Crédit Mutuel Alliance Fédérale (Bobigny) depuis 58.188.207.52
[2013-03-29] Noam Grand, tentative de PIN 4820 à AXA Banque (Poissy) depuis 186.54.107.131

它甚至可以与日语一起使用,数据集令人印象深刻,并且它已经取代了我工具包中我心爱的faker

我现在在PYTHONSARTUP中使用这个:

PYTHONSARTUP: 设置shell变量的脚本

try:
    import mimesis
except ImportError:
    pass
else:
    from mimesis.locales import Locale
    from mimesis import Generic

    class FakeProvider:
        def __init__(self, provider):
            self.provider = provider
            self.count = 1

        def __dir__(self):
            return dir(self.provider)

        def __repr__(self):
            return f"FakeProvider({repr(self.provider)})"

        def __getattr__(self, name):
            subprovider = getattr(self.provider, name)
            wrapper = lambda *args, count=1, **kwargs: self.call_faker(subprovider, *args, **kwargs)
            wraps(subprovider)(wrapper)
            return wrapper

        def __call__(self, count=1):
            self.count = count
            return self

        def call_faker(self, subprovider, *args, **kwargs):
            if self.count == 1:
                return subprovider(*args, **kwargs)
            else:
                return [subprovider(*args, **kwargs) for _ in range(self.count)]
            self.count = 1

    class Fake(object):

        def __init__(self, locale=Locale.EN):
            self.configure(locale)

        def get_factory(self, locale=Locale.EN, _cache={}):
            if isinstance(locale, str):
                locale = getattr(Locale, locale.upper())
            if locale in _cache:
                return _cache[locale]
            return Generic(locale=locale)

        def configure(self, locale=Locale.EN):
            self.factory = self.get_factory(locale)

        @property
        def fr(self):
            return self.get_factory(locale="fr")

        @property
        def en(self):
            return self.get_factory(locale="en")

        def __getattr__(self, name):
            return FakeProvider(getattr(self.factory, name))

        def __dir__(self):
            return ["fr", "en", *dir(self.factory)]


    fake = Fake()

在python会话中,我可以执行:

>>> fake.food(10).fruit()
['Kahikatea', 'Canistel', 'Hackberry', 'Bilberry', 'Genip', 'Cocona', 'Limeberry', 'Blueberry', 'Bilimbi', 'Redcurrant']

是时候了

如果必须在代码中提供日期,有两种方法可以使其易于测试。
第一,通过参数传递日期。例如:

def create_article(text, time_provider=datetime.datetime.now):
    Article(text, created_at=date_provider())

第二:使用黑魔法,在测试期间修补时间。

认识一下 freezegun,可以让你暂停时间:

>>> import datetime
... from freezegun import freeze_time
...
... def calendar_with_cheesy_quote():
...     print(f"{datetime.date.today():%B %d, %Y}")
...     print("Each day provide it's own gift")
...
... with freeze_time("2012-01-14"):
...     calendar_with_cheesy_quote()
...
January 14, 2012
Each day provide it's own gift

它与 pytest 集成,因此您可以装饰测试并假装您是 Timelord:

@freeze_time("Jan 14th, 2020") # you can use a nice format here too
def test_sonic_screwdriver_in_overrated_show_there_I_said_it():
    ...

它可以从某个点开始时间,然后继续流动:

@freeze_time("2012-01-14", tick=True)
def test_turn_it_on_turn_it_on_turn_it_on_again():
    # the clock starts again here, and the first value is 2012-01-14 
    # at 00:00:00 then whatever number of seconds goes by after that

可以设置每次请求时间时增加 15 秒:

@freeze_time("2012-01-14", auto_tick_seconds=15)
def test_wheeping_angels_when_you_blink():
    # the clock starts again here, and the first value is 2012-01-14 
    # at 00:00:00 then every call to datetimes add 15 seconds

或手动控制时间:

 with freeze_time("2012-01-14") as right_here_right_now:
    right_here_right_now.tick(3600) # add 1H next time we request time
    right_here_right_now.move_to(other_datetime) # jump in time

I/O

你将不得不读取和写入一些东西,如文件套接字。这些都是非常不可靠的副作用,所以嘲笑可能是你的第一个赌注。但是有一些专门的工具可以让你的生活更轻松。

众所周知,stdlib 附带了 tempfile 模块,该模块可以生成自动清理的临时文件和目录:

>>> import tempfile
... from pathlib import Path
...
... with tempfile.TemporaryDirectory() as temp_dir:
...     temp_path = Path(temp_dir)
...
...     (temp_path / 'subdir1').mkdir()
...     (temp_path / 'subdir2').mkdir()
...
...     (temp_path / 'file1.txt').write_text('This is file 1')
...     (temp_path / 'file2.txt').write_text('This is file 2')
...
...     print(f'Now you see me: {temp_dir}')
...     print(f'Contents: {list(temp_path.iterdir())}')
...
... print(f"Now you don't: {Path(temp_dir).exists()}")
Now you see me: /tmp/tmp67e3hjr2
Contents: [PosixPath('/tmp/tmp67e3hjr2/file1.txt'), PosixPath('/tmp/tmp67e3hjr2/subdir2'), PosixPath('/tmp/tmp67e3hjr2/subdir1'), PosixPath('/tmp/tmp67e3hjr2/file2.txt')]
Now you don't: False

即使在测试之外也很有用,但如果由于某种原因您不想触摸硬盘驱动器,则有pyfakefs

from pyfakefs.fake_filesystem_unittest import Patcher
from pathlib import Path
import os

with Patcher() as patcher:
    fs = patcher.fs
    temp_path = Path(os.path.join(os.sep, 'tmp', 'tempdir'))
    fs.create_dir(temp_path)

    (temp_path / 'subdir1').mkdir()
    (temp_path / 'subdir2').mkdir()

    (temp_path / 'file1.txt').write_text('This is file 1')
    (temp_path / 'file2.txt').write_text('This is file 2')

    print(f'Now you see me: {temp_path}')
    print(f'Contents: {list(temp_path.iterdir())}')

    fs.remove_object(temp_path)

print(f"Now you don't: {temp_path.exists()}"")

就像冷冻枪一样,这是一个很大的黑魔法,但很有用。它将修补所有文件系统调用,并在内存中创建一个虚构的 FS,因此您的文件操作永远不会离开 RAM。
请注意,使用真实文件进行测试将允许您处理边缘情况,例如在多个分区上具有符号链接和树,这有时会令人惊讶。为正确的工作提供正确的工具等等。

由于我们处于内存领域,因此您可以使用 sqlite处理数据库

import sqlite3
connection = sqlite3.connect(':memory:')
cursor = connection.cursor()

cursor.execute('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)')
cursor.execute('INSERT INTO test (name) VALUES ("Alice")')
cursor.execute('INSERT INTO test (name) VALUES ("Bob")')
connection.commit()

cursor.execute('SELECT * FROM test')
rows = cursor.fetchall()
print('SQLite in-memory database contents:')
for row in rows:
    print(row)

这将在不接触磁盘的情况下创建一个数据库,这将是临时的,而且速度非常快。

最后,网络呢?

好吧,网络的工具和应用一样多,你会看到fakker redis, HTTP API, 中间人拦截,甚至全面的网络控制

不过,您可能会感兴趣的一个是 VCR.py,这是一个 Python 库,可以记录您的 HTTP 请求,并可以将其响应重放给您。

编写一个执行请求的测试,用@pytest.mark.vcr 标记它:

import pytest
import requests

@pytest.mark.vcr
def test_single():
    assert requests.get("http://httpbin.org/get").text == '{"get": true}'

然后,针对真实网络运行一次:

pytest --record-mode=once test_network.py

下次您正常运行测试(移除 --record-mode ), VCR 将为您重播网络响应,并且您不会接触网络。

快照

我已经将一半的测试写入委托给 ChatGPT,然后在其中触发一个断点,这样我就可以窃取调用结果并将它们转换为断言。这是一个很好的技巧,它在许多情况下都有效。

当然,有一种方法可以进一步自动化!
inline-snapshot, 允许你写占位符,表示你的测试结果应该出现在哪里:

from inline_snapshot import snapshot

def test_something():
    assert 1548 * 18489 == snapshot() # snapshot() is the placeholder

然后运行测试以记录结果:

pytest --inline-snapshot=create

你会得到用以下值填充的测试:

神奇,你会发现文件自动变成了下面这个:

from inline_snapshot import snapshot

def test_something():
    assert 1548 * 18489 == snapshot(28620972)

您现在可以检查这些值是否有意义(不要相信它们,您的代码可能有问题),如果它们有效,请将它们提交到 git。下次正常运行它们(移除–inline-snapshot )时,快照中的数据将被透明地使用并据以测试。

您可以使用 --inline-snapshot=update 来更新自动更改的快照, --inline-snapshot=fix 仅更改不再通过的测试,或 --inline-snapshot=review 手动查看更改并批准更改,甚至 --inline-snapshot=disable 返回到原始值,并加快速度或检查快照是否是问题的原因。


评论是交互式的,非常方便:在这里插入图片描述
你可能会问,那些 external() 是什么。如果您不希望在测试代码中使用快照,则可以使用它。数据可能非常大,或者是一些非文本格式,会弄乱您的文件。您可以将测试结果标记为outsource

from inline_snapshot import outsource, snapshot

def test_a_big_fat_data():
    assert [
        outsource("long text\n" * times) for times in [50, 100, 1000]
    ] == snapshot()

然后,当你用 --inline-snapshot=create ,它会变成这样:

from inline_snapshot import outsource, snapshot, external

def test_a_big_fat_data():
    assert [
        outsource("long text\n" * times) for times in [50, 100, 1000]
    ] == snapshot(
        [
            external("362ad8374ed6*.txt"),
            external("5755afea3f8d*.txt"),
            external("f5a956460453*.txt"),
        ]
    )

文件存储在默认的 pytest 配置目录中,可以使用 --inline-snapshot=trim 删除过时的文件。

我喜欢这个库,因为您可以将快照的值直接放入代码中。我认为对于许多测试来说,它比从文件加载它更有意义。
但它有一个很大的局限性:你只能测试可以使用 repr() 的输出重新创建的东西,所以主要是 Python 内置的,如字符串、整数、列表字典和一些对象,如 datetime。如果你需要更复杂的对象,你就不走运了。
在这种情况下,您可以使用更强大的 pytest-insta,它允许 pickle作为序列化格式,因此您可以拍摄几乎所有内容的快照。如果你做不到,你可以创建自己的格式化程序,并使用像 cloudpickle 这样的黑魔法真正将任何怪物变成平面字节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1850898.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytest测试框架pytest-sugar插件生成进度条

Pytest提供了丰富的插件来扩展其功能,介绍下插件pytest-sugar,可以帮助我们在控制台中显示彩色的测试结果和进度条,提供失败的堆栈回溯信息。 为了使用 pytest-sugar,需要满足以下条件: Python 3.8 或更高版本pytest…

Nominatim免费的地址解析,逆地址解析,OpenStreetMap开源地图数据【全网最全】

视频学习地址 国内的一些地址解析供应商的API都开始付费了,就想找个免费的地址解析和逆地址解析的应用,最终选择了Nominatim OpenStreetMap 文章目录 一、选型1-1、数据源1-2、地理编码引擎2-1、初尝Nominatim2-1-1、地址解析2-1-2、逆地址解析 2-2、OS…

【MMSegmentation 环境配置】

MMSegmentation 环境配置 1. 创建python 环境2. 安装pytorch3. 安装MMCV4. 安装 MMSegmentation.5. 测试是否安装成功 1. 创建python 环境 conda create --name openmmlab python3.8 -y conda activate openmmlab2. 安装pytorch On GPU platforms: conda install pytorch tor…

平凉特色小吃,味蕾的诱惑之旅

平凉,这座历史悠久的城市,不仅拥有深厚的文化底蕴,更有着让人垂涎欲滴的特色小吃。每一种小吃都承载着当地人的情感与记忆,成为了平凉独特的饮食符号。平凉特色小吃酿皮更是别具风味。爽滑透明的凉皮,配上香辣可口的调…

动态规划——买卖股票的最佳时机含冷冻期

1、题目链接 leetcode 309. 买卖股票的最佳时机含冷冻期 2、题目分析 该题有我们可以定义三种状态,买入状态,可交易状态 ,冷冻期状态 我们可以建立一个n*3的二维数组来表示这三种状态: 根据这个图可以看出, 可以从…

JAVA同城服务场馆门店预约系统支持H5小程序APP源码

📱一键预约,畅享无忧体验🏢 🚀一、开启预约新纪元 在繁忙的都市生活中,我们常常因为时间紧张而错过心仪的门店或场馆服务。然而,有了“门店场馆预约小程序”,这些问题都将迎刃而解。这款小程序…

前端编程语言——JS语言结构、函数、数组、字符串、日期、对象、定时器(2)

0、前言: 这篇文章记录的是我自己的学习笔记。在python中通过input来获取输入,在JS中用prompt(),来获取输入。写JS代码要记得每个代码结束要加上分号。 1、JS编程语言结构: 顺序结构:从上往下依次执行分支结构&#…

2021-03-29:加密与解密

前段时间导师分配的任务主要是看《加密与解密》这本书,“书本写的很详细,认真看会看懂的!” 是的啊,书本写的很详细,可是作为一个没基础的小白看起来还是挺吃力的,概念一个接一个的出现,虽然看…

每日待办事项提醒用什么便签app比较好?

在快节奏的现代生活中,我们经常需要记住各种事项,如会议、预约、购物清单等。这时,一个高效的便签App就显得尤为重要,可以帮助我们有效地管理日常任务和待办事项。而每日待办事项提醒用什么便签app比较好?面对市场上众…

Vulnhub--AI: WEB: 2

渗透复现 平台框架存在目录穿越漏洞,利用该漏洞读取敏感信息 Ping功能点绕过,进行命令执行操作,反弹shell无果后,利用目录穿越漏洞遍历敏感API,读取到用户SSH登录凭证 SSH登录进行Linux lxd容器提权 知识扩展 目录…

【Proteus仿真】【Arduino单片机】汽车倒车报警系统设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduinno单片机控制器,使用LCD1602液晶、按键、继电器电机模块、DS18B20温度传感器、蜂鸣器LED、HCSR04超声波等。 主要功能: 系统运行后,LCD16…

【PyCUDA安装问题集锦:网站失效】

windows 下 pycuda 安装问题 问题一 安装不上的主要问题:pip默认安装最新版,导致pycuda版本和系统版本不对应。 1.先查看cuda版本 nvidia-smi2.查看需要安装的虚拟环境的python版本(如python3.8) 3.前往下载python扩展包&…

Django从入门到精通:First [Django版本.Python面向对象.Web基础.创建Django项目]

文章目录 Django初学者指南1 Django简介1.1 Django的历史1.2 使用Django的知名网站1.4 Django的主要特点1.5 Django的工作原理 2 Django 版本选择2.1 Django 支持的 Python 版本2.2 Django 版本 3 Django 开发 Web 程序3.1 Python知识点3.1.1 Python 函数3.1.2 Python 面向对象…

【LeetCode热题 100】螺旋矩阵

leetcode原地址:https://leetcode.cn/problems/spiral-matrix/description 描述 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8…

Rcmp: Reconstructing RDMA-Based Memory Disaggregation via CXL——论文阅读

TACO 2024 Paper CXL论文阅读笔记整理 背景 RDMA:RDMA是一系列协议,允许一台机器通过网络直接访问远程机器中的数据。RDMA协议通常固定在RDMA NIC(RNIC)上,具有高带宽(>10 GB/s)和微秒级延…

Apple - Launch Services Programming Guide

本文翻译整理自:Launch Services Programming Guide https://developer.apple.com/library/archive/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCIntro/LSCIntro.html#//apple_ref/doc/uid/TP30000999-CH201-TP1 文章目录 一、导言谁应该阅读此文档…

考前刷题练手感(北航期末往年数据结构编程题)

本次因为是考前一天极速刷题,所以没有讲解,若有问题可私信。 目录 一、 查找同时空人员二、 老鼠回家-无回路三、函数调⽤关系四、东二食堂模拟五、栈帧 一、 查找同时空人员 【问题描述】 假设一共有6个手机基站,都具有记录手机连接基站状…

动手学深度学习(Pytorch版)代码实践 -深度学习基础-10权重衰减

10权重衰减 """ 正则化是处理过拟合的常用方法:在训练集的损失函数中加入惩罚项,以降低学习到的模型的复杂度。 保持模型简单的一个特别的选择是使用L2惩罚的权重衰减。这会导致学习算法更新步骤中的权重衰减。 """impor…

揭秘古代手术工具与技术:从中国起源的医疗奇迹

在人类历史的长河中,医学的发展一直是推动社会进步的重要力量。而手术作为医学的一个重要分支,其发展历程同样充满了传奇色彩。今天,我们将带您走进古代手术的世界,揭秘那些令人惊叹的手术工具和技术。 这把手术刀出土于河北西村遗…

sqlmap使用以及GUI安装

下载 GUI版地址: GitHub - honmashironeko/sqlmap-gui: 基于官版本 SQLMAP 进行人工汉化,并提供GUI界面及多个自动化脚本 GUI使用 可以点击.bat启动 如果点击.bat启动不了就在这里打开cmd,输入对应的.bat来启动 linux安装 地址:sqlmap: automatic SQL injection…