0

0

在 Python 中使用 Factory-Boy 生成假批量数据

王林

王林

发布时间:2024-07-25 10:10:44

|

882人浏览过

|

来源于dev.to

转载

您经常需要假数据来进行测试。当然,您可以在测试中使用一些示例数据。但是,如果您需要数百条甚至数千条测试数据怎么办?那么创建和维护就会变得乏味。有时,您只需要程序中类的假版本,但您希望它是行为类似于真实类的真实数据。工厂和假货让这一切成为可能。

在本文中,我们将使用 python 中的 factory-boy 包来生成假数据。

请确保使用命令 pip installfactory-boy==2.12.0 安装正确版本的factory-boy

让我们尝试使用 factory boy 生成一些假数据

import factory #import factory-boy
name = factory.faker('name') #create faker object with 'name' provider
for _ in range(5):
    print(name.generate()) #generate and print 5 unique names

结果:

立即学习Python免费学习笔记(深入)”;

在 Python 中使用 Factory-Boy 生成假批量数据

factory.faker() 接受一个称为provider 的参数,用于生成数据类型,例如“name”provider 将用于生成名称,“email”provider 将用于生成电子邮件等。
让我们尝试使用更多的提供商

import factory #import factory-boy
name = factory.faker('name') #create faker object with 'name' provider
country = factory.faker('country') #create faker object with 'country' provider
email = factory.faker('email') #create faker object with 'email' provider

for _ in range(3):
    print(f'''
my name is {name.generate()}
i live in {country.generate()}
reach out to me at {email.generate()}''')

结果:

立即学习Python免费学习笔记(深入)”;

在 Python 中使用 Factory-Boy 生成假批量数据

除了faker类之外,factory-boy还有一个模块fuzzy,我们来看看它是如何工作的

import factory #import factory-boy module
import factory.fuzzy #import fuzzy module
name = factory.faker('name') #create faker object with 'name' provider
gender = factory.fuzzy.fuzzychoice(choices=['girl','boy']) #create fuzzychoice object which selects from the given options
grade = factory.fuzzy.fuzzyfloat(30,80) #create fuzzyfloat object which generates a random float number between the lower and upper limit
age = factory.fuzzy.fuzzyinteger(12,18) #create fuzzyinteger object which generates a random float numbeer between the lower and upper limit

for _ in range(3):
    print(f'''my name is {name.generate()}, i am a {gender.fuzz()} 
i got a grade of {grade.fuzz():.2f}% and my age is {age.fuzz()}
''')

结果:

立即学习Python免费学习笔记(深入)”;

fuzzy

要了解有关模糊模块中不同类的更多信息以及 faker 提供者的各种选项,请访问:https://factoryboy.readthedocs.io/en/stable

现在我们知道了 factory-boy 如何模仿数据,让我们尝试模仿通常用于为 flask、django 等应用程序创建数据库表的数据模型。
对于这个项目,创建一个requirements.txt并粘贴以下内容,然后使用pip3 install -rrequirements.txt安装所有必需的包

# pin dependancies that might cause breakage
werkzeug==2.1.2
sqlalchemy==1.4.46

# dependencies for this project
flask==2.1.2
flask-sqlalchemy==2.5.1

# testing dependencies
nose==1.3.7
pinocchio==0.4.3
coverage==6.3.2
factory-boy==2.12.0
pylint==2.14.0

由于我们将使用 nosetest 运行单元测试,请查看我之前的文章 - “使用 unittest 和 nose 在 python 中进行测试驱动开发” (https://dev.to/h4ck3rd33p/test-driven-development-in-python-使用-unittest-and-nosetest-24ck)

创建一个用于nosetests配置的setup.cfg文件并粘贴以下内容

[nosetests]
verbosity=2
with-spec=1
spec-color=1
with-coverage=1
cover-erase=1
cover-package=models

[coverage:report]
show_missing = true

现在创建两个文件夹模型,其中包含 account.py 中的数据模型和 __init__.py 中的基本设置,以及包含工厂模型的测试,该工厂模型将模仿factories.py中的实际模型以及用于测试的相关单元测试test_account.py 中的应用程序.
最终你的文件夹结构应该如下所示:

.
├── models
│   ├── account.py
│   └── __init__.py
├── requirements.txt
├── setup.cfg
└── tests
    ├── factories.py
    └── test_account.py

2 directories, 6 files

假设我们需要测试一个处理客户帐户的数据模型。我们将从创建这个数据模型开始。我们将使用一种名为 sqlalchemy 的流行对象关系映射器,因此我们创建 sqlalchemy 类的数据库实例。现在我们构建我们的模型。我们创建一个名为 accounts 的类,它继承自 sqlalchemy 的基本模型。现在我们可以添加列,这些列将表示为类变量。我们添加一个id。它将作为非信息承载键,因此我们将 id 标记为主键。我们将名称添加为字符串,将电子邮件字段添加为字符串。我们还添加电话号码作为字符串。我们将该电话号码设置为可选,因此我们将 nullable 设置为 true。让我们添加一个布尔字段来确定该帐户是否被禁用,并将默认值设置为 false。最后,我们将添加一个日期连接列作为 datetime 并将其设置为可选。

模型 > __init__.py

"""
data models
"""
from flask import flask
from flask_sqlalchemy import sqlalchemy

app = flask(__name__)
app.config['sqlalchemy_track_modifications'] = false
app.config['sqlalchemy_database_uri'] = 'sqlite:///test.db'
db = sqlalchemy(app)

模型 > account.py

"""
account class
"""
import logging
from sqlalchemy.sql import func
from models import db

logger = logging.getlogger()


class datavalidationerror(exception):
    """used for an data validation errors when deserializing"""


class account(db.model):
    """ class that represents an account """

    id = db.column(db.integer, primary_key=true)
    name = db.column(db.string(64))
    email = db.column(db.string(64))
    phone_number = db.column(db.string(32), nullable=true)
    disabled = db.column(db.boolean(), nullable=false, default=false)
    date_joined = db.column(db.date, nullable=false, server_default=func.now())

    def __repr__(self):
        return '' % self.name

    def to_dict(self) -> dict:
        """serializes the class as a dictionary"""
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def from_dict(self, data: dict) -> none:
        """sets attributes from a dictionary"""
        for key, value in data.items():
            setattr(self, key, value)

    def create(self):
        """creates a account to the database"""
        logger.info("creating %s", self.name)
        db.session.add(self)
        db.session.commit()

    def update(self):
        """updates a account to the database"""
        logger.info("saving %s", self.name)
        if not self.id:
            raise datavalidationerror("update called with empty id field")
        db.session.commit()

    def delete(self):
        """removes a account from the data store"""
        logger.info("deleting %s", self.name)
        db.session.delete(self)
        db.session.commit()

    ##################################################
    # class methods
    ##################################################

    @classmethod
    def all(cls) -> list:
        """returns all of the accounts in the database"""
        logger.info("processing all accounts")
        return cls.query.all()

    @classmethod
    def find(cls, account_id: int):
        """finds a account by it's id
        :param account_id: the id of the account to find
        :type account_id: int
        :return: an instance with the account_id, or none if not found
        :rtype: account
        """
        logger.info("processing lookup for id %s ...", account_id)
        return cls.query.get(account_id)

现在让我们创建模仿原始 account 类的假类。我们将其命名为 accountfactory。还创建一个内部类 meta 并创建一个属性模型并将其设置为 account,这将使 factory-boy 确切地知道它必须模仿哪个数据类,因此 accountfactory 现在将自动拥有 account 类具有的所有方法。

测试 > 工厂.py

"""
accountfactory class using factoryboy
"""
import factory
from datetime import date
from factory.fuzzy import fuzzychoice, fuzzydate
from models.account import account

class accountfactory(factory.factory):
    """ creates fake accounts """

    class meta:
        model = account

    id = factory.sequence(lambda n: n)
    name = factory.faker("name")
    email = factory.faker("email")
    phone_number = factory.faker("phone_number")
    disabled = fuzzychoice(choices=[true, false])
    date_joined = fuzzydate(date(2008, 1, 1))

id = factory.sequence(lambda n: n) 将生成连续的数字序列 0,1,2...

现在让我们编写单元测试来测试我们的 accountfactory

测试装置:这些是在运行测试用例之前和之后设置系统状态的方法。

  • setupclass(cls):这用于在运行类中的任何测试用例之前设置系统状态。在我们的示例中,它正在创建数据库和表。

  • teardownclass(cls):用于在运行当前类中的所有测试用例后清理系统。在我们的示例中,它删除所有剩余的测试数据并断开与数据库的连接。

  • setup(self):这用于在运行单个测试用例之前设置系统。在我们的示例中,它正在删除数据并保存它。

  • teardown(self):这用于在运行单个测试用例后重置系统状态。在我们的示例中,它正在关闭当前数据库会话。

  • setupall:这用于在进入任何类之前设置系统状态。 我们在示例中没有使用这个

  • teardownall:这用于在完成所有类后重置系统状态。 我们在示例中没有使用这个

测试 > test_account.py

"""
Test Cases TestAccountModel
"""

from random import randrange
from unittest import TestCase
from models import db
from models.account import Account, DataValidationError
from factories import AccountFactory


class TestAccountModel(TestCase):
    """Test Account Model"""

    @classmethod
    def setUpClass(cls): #Runs before running any unit test
        """ Create table """
        db.create_all()  # make our sqlalchemy tables

    @classmethod
    def tearDownClass(cls): #Runs after running all the tests
        """Delete test data and Disconnext from database"""
        db.session.query(Account).delete()
        db.session.close()

    def setUp(self): #Runs before running every individual test
        """Drop the table"""
        db.session.query(Account).delete()
        db.session.commit()

    def tearDown(self): #Runs after ruunning every individual test
        """Remove the session"""
        db.session.remove()

    ######################################################################
    #  T E S T   C A S E S
    ######################################################################

    def test_create_all_accounts(self):
        """ Test creating multiple Accounts """
        for _ in range(10):
            account = AccountFactory()
            account.create()
        self.assertEqual(len(Account.all()), 10)

    def test_create_an_account(self):
        """ Test Account creation using known data """
        account = AccountFactory()
        account.create()
        self.assertEqual(len(Account.all()), 1)

    def test_repr(self):
        """Test the representation of an account"""
        account = Account()
        account.name = "Foo"
        self.assertEqual(str(account), "")

    def test_to_dict(self):
        """ Test account to dict """
        account = AccountFactory()
        result = account.to_dict()
        self.assertEqual(account.name, result["name"])
        self.assertEqual(account.email, result["email"])
        self.assertEqual(account.phone_number, result["phone_number"])
        self.assertEqual(account.disabled, result["disabled"])
        self.assertEqual(account.date_joined, result["date_joined"])

    def test_from_dict(self):
        """ Test account from dict """
        data = AccountFactory().to_dict()
        account = Account()
        account.from_dict(data)
        self.assertEqual(account.name, data["name"])
        self.assertEqual(account.email, data["email"])
        self.assertEqual(account.phone_number, data["phone_number"])
        self.assertEqual(account.disabled, data["disabled"])

    def test_update_an_account(self):
        """ Test Account update using known data """
        account = AccountFactory()
        account.create()
        self.assertIsNotNone(account.id)
        account.name = "Rumpelstiltskin"
        account.update()
        found = Account.find(account.id)
        self.assertEqual(found.name, account.name)
        self.assertIsNotNone(account.id)
        account.name = "Rumpelstiltskin"
        account.update()
        found = Account.find(account.id)
        self.assertEqual(found.name, account.name)

    def test_invalid_id_on_update(self):
        """ Test invalid ID update """
        account = AccountFactory()
        account.id = None
        self.assertRaises(DataValidationError, account.update)

    def test_delete_an_account(self):
        """ Test Account update using known data """
        account = AccountFactory()
        account.create()
        self.assertEqual(len(Account.all()), 1)
        account.delete()
        self.assertEqual(len(Account.all()), 0)

阅读评论以了解有关测试用例的更多信息。现在让我们运行鼻子测试

nosetests

恭喜!所有测试用例都已通过,因此我们可以得出结论,我们的 accountfactory 与 account 类完全相同,并且已使用 factory-boy 使用虚假数据测试了应用程序!

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

760

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

639

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

762

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

619

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1285

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

709

2023.08.11

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

72

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 3.7万人学习

Django 教程
Django 教程

共28课时 | 3.2万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号