首页 > 编程语言 >使用 Python 进行测试(7)...until you make it

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

时间:2024-06-22 21:00:43浏览次数:3  
标签:... temp Python make snapshot import path self

总结

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

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

>>> 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 这样的黑魔法真正将任何怪物变成平面字节。

标签:...,temp,Python,make,snapshot,import,path,self
From: https://blog.csdn.net/qq_41068877/article/details/139870391

相关文章

  • 2024华为OD机试真题- 找出作弊的人-(C++/Python)-C卷D卷-100分
     2024华为OD机试题库-(C卷+D卷)-(JAVA、Python、C++) 题目描述公司组织了一次考试,现在考试结果出来了,想看一下有没人存在作弊行为,但是员工太多了,需要先对员工进行一次过滤,再进一步确定是否存在作弊行为。过滤的规则为:找到分差最小的员工ID对(p1,p2)列表,要求p1<......
  • python-opencv批量处理图像文件(附代码)
        这里以cifar100数据集为例。cifar100数据集保存在train文件夹中,其中一共有100类图片,每类图片被保存在不同的子文件夹中,每类图片500张,其具体文件夹如下。    首先要引入cv2和os库,接着还要提前设置好图像保存路径和原图像文件路径。如果这里设置的不对的话......
  • python socket写客户端
    客户端开发流程1、创建客户端套接字对象2、和服务端套接字建立连接3、发送数据4、接收数据5、关闭客户端套接字注意:客户端是指运行在用户的设备上,服务端是指运行在服务器设备上的,专门为客户端提供数据服务socket类的使用1、导入socket模块importsocket2、使用s......
  • 538个代码示例!麻省理工教授的Python程序设计+人工智能案例实践
    Python简单易学,且提供了丰富的第三方库,可以用较少的代码完成较多的工作,使开发者能够专注于如何解决问题而只花较少的时间去考虑如何编程。此外,Python还具有免费开源、跨平台、面向对象、胶水语言等优点,在系统编程、图形界面开发、科学计算、Web开发、数据分析、人工智能等方面......
  • python学习笔记-10
    面向对象编程-下1.私有化属性语法:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。使用私有化属性的场景:1.把特定的一个属性隐藏起来,不让类的外部进行直接调用。2.不让属性的值随意改变。3.不让子类继承。classPerson():def__init__(self):......
  • 【球类识别系统】图像识别Python+卷积神经网络算法+人工智能+深度学习+TensorFlow
    一、介绍球类识别系统,本系统使用Python作为主要编程语言,基于TensorFlow搭建ResNet50卷积神经网络算法模型,通过收集'美式足球','棒球','篮球','台球','保龄球','板球','足球','高尔夫球','曲棍球','冰球','橄榄球',&#......
  • 【球类识别系统】图像识别Python+卷积神经网络算法+人工智能+深度学习+TensorFlow
    一、介绍球类识别系统,本系统使用Python作为主要编程语言,基于TensorFlow搭建ResNet50卷积神经网络算法模型,通过收集‘美式足球’,‘棒球’,‘篮球’,‘台球’,‘保龄球’,‘板球’,‘足球’,‘高尔夫球’,‘曲棍球’,‘冰球’,‘橄榄球’,‘羽毛球’,‘乒乓球......
  • python 快速排序
     快速排序快速排序是一种非常高效的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。 以下是一个使用Python实现的快速排序的示例代码: pythond......
  • AI生成Python之语句练习题
    对应文章:Python学习记录#2、语句涉及:条件语句(if-elif-else)、循环语句(while、for)。目录练习题1.猜数字游戏2. 斐波那契数列(难)3. 九九乘法表4.列表过滤5. 字符串反转练习题1.猜数字游戏        编写一个程序,让计算机随机选择一个1到100之间的整数,......
  • Python基础语法,非常值得新手小白学习!
    Python基础语法(一)Python的特点1. 简单Python是一种代表简单思想的语言。2. 易学Python有极其简单的语法。3. 免费、开源Python是FLOSS(自由/开放源码软件)之一。4. 高层语言使用Python编写程序时无需考虑如何管理......