返回介绍

14.3 fixture replacement

发布于 2024-01-21 17:11:03 字数 7321 浏览 0 评论 0 收藏 0

14.3.1 什么是测试配置器

在第 8 章中,我们学习了测试的相关知识,但在实际编写测试代码的过程中,有时必须依赖于模块和数据,这种时候就要事先准备测试数据。

Django 默认能够将测试数据事先读入数据库。Django 的测试配置器是一种专门用来让 Django 读入数据库的特定格式数据文件。除测试之外,测试配置器还可以用来导入开发初期阶段需要用到的数据。

◉ 用 loaddata 命令读取配置器

下面我们手动创建一个用于配置器的数据文件,然后读取它。这里需要利用前面生成的 Poll 模型。我们首先在工程目录下创建名为 polls.json 的文件,文件内容如 LIST 14.20 所示。

LIST 14.20 polls.json

[
  {
  "model": "polls.poll",
  "pk": 1,
  "fields": {
    "question_text": "Why not use the fixture",
    "pub_date": "2011-11-01 00:00:00Z"
  }
  },
  {
  "model": "polls.poll",
  "pk": 2,
  "fields": {
    "question_text": "Why not use the fixture2",
    "pub_date": "2011-11-02 00:00:00Z"
  }
  }
]

pk 指模型的 primary key 的 ID。接下来将这个数据文件加载到数据库中(LIST 14.21)。

LIST 14.21 执行 loaddata

$ python manage.py loaddata polls.json

执行完毕后,可以在数据库的 polls_poll 表中看到上述配置器提供的数据。各位可以使用 SQL 等进行查看。

◉ 配置器文件的存放位置

在工程开始阶段,将配置器文件放在工程根目录下并无大碍,但是随着应用规模越来越大,文件自然也越来越多,放在工程根目录下将很难维护。这种时候就需要修改配置器文件的存放位置并进行整理。整理有以下 2 个方法。

· 存放在对应应用的 fixtures 目录下

· 存放在 settings.py 的 FIXTURE_DIRS 指定的目录下

比如我们在应用目录下创建了 fixtures 目录(例如 polls/fixtures/polls.json),我们就可以将该应用需要读取的配置器文件放在这里。这样一来,只需要执行 python manage.py loaddata polls.json 命令即可读取配置器文件。另一种就是设置 settings.py 中的 FIXTURE_DIRS,该变量所指目录下存放的配置器文件将被视为读取对象。如果想把任意目录作为读取对象,建议使用这种方法。LIST 14.22 是添加与 manage.py 同级的 fixtures 目录的方法。

LIST 14.22 FIXTURE_DIRS 的设置

FIXTURE_DIRS = [
  os.path.join(BASE_DIR, 'fixtures'),
]

◉ 在 TestCase 中使用配置器

Django 的 TestCase 能够自动读取配置器的信息。只要在 TestCase 的属性里设置了 fixtures,所指定的配置器文件就会被自动读取。在 LIST 14.23 中,我们读取了名为 polls.json 的配置器,使得测试用例可以使用配置器内记录的数据。

LIST 14.23 使用配置器的 TestCase

from django.test import TestCase
from polls.models import Poll
class PollsTestCase(TestCase):
  fixtures = ['polls.json']
  def setUp(self):
    # 一般的测试定义
  def testPoll(self):
    # 使用配置器的测试

◉ 用dumpdata 生成配置器文件

很多时候,纯手动编写配置器文件并不现实(比如需要大量档案或者多种不同情况的数据时)。Django 有生成 fixutre 文件的辅助功能,那就是 dumpdata 命令。dumpdata 命令可以用数据库里的数据生成配置器文件(LIST 14.24)。

LIST 14.24 执行 dumpdata

$ python manage.py dumpdata polls > polls.json

执行完毕后生成了名为 polls.json 的配置器文件。内容与之前用 loaddata 加载的内容相同。

14.3.2 几种不便使用默认配置器的情况

配置器用起来很方便,但编写测试代码时我们也会遇到配置器碍事的情况,下面就举几个例子来说明一下。

◉ 日期字段会变成特定日期

我们再来看看上面提到的 polls.json,会发现 pub_data 列的日期是固定的(LIST 14.25)。

LIST 14.25 polls.json

[
  {
  "model": "polls.poll",
  "pk": 1,
  "fields": {
    "question_text": "Why not use the fixture",
    "pub_date": "2011-11-01 00:00:00Z"
  }
  },
  {
  "model": "polls.poll",
  "pk": 2,
  "fields": {
    "question_text": "Why not use the fixture2",
    "pub_date": "2011-11-02 00:00:00Z"
  }
  }
]

比如我们的测试代码希望用到包含“今天的日期”的 Poll 模型时,就需要将配置器文件中的 pub_data 修改成“今天的日期”才行。在这种需要用到“今天的日期”或类似数据的情况下,就不适合用配置器来准备测试数据。

◉ 配置器不容易维护

举个例子,我们给 Poll 模型添加新的属性(列)时,需要对 polls.json 的所有数据添加属性。这将是一个非常费劲的工作。特别是在开发新的应用时,模型的修改往往很频繁,要想维护配置器文件使之能跟上模型的变化,成本通常不低。

可见,配置器在事先准备数据方面显得十分便捷,但它准备的数据缺乏灵活性和可维护性。

接下来我们将学习一个能解决上述这些问题的工具——factory_boy。

14.3.3 如何使用 factory_boy

factory_boy2 可以帮助我们生成更加灵活、更易维护的配置器。从名字可以看出来,它是 Ruby 经典工具 factory_girl3 的 Python 版。

2 https://github.com/rbarrois/factory_boy

3 https://github.com/thoughtbot/factory_girl

Django 的配置器是基于文件来准备数据,而 factory_boy 是生成对应于数据模型的 Factory 类,因此它能够动态且灵活地为我们准备测试数据。这种不以文件形式提供配置器,而是提供能带来同样效果的数据模型的工具称为 fixture replacement。除支持 Django 外,factory_boy 还支持其他 O/R 映射工具。

这里我们用 pip 安装 factory_boy(LIST 14.26)。

LIST 14.26 安装 factory_boy

$ pip install factory-boy

本书使用了 factory_boy 的 2.4.1 版本。

◉ Factory

Factory 对于将目标对象实例化时所需的一系列属性进行了定义。在命名 Factory 类时,需要让使用者能够通过我们赋予的名称推测出其目标对象的类(LIST 14.27)。

LIST 14.27 PollFactory 类

import factory
from django.utils import timezone
from polls.models import Poll
class PollFactory(factory.django.DjangoModelFactory):
  question_text = 'factory question'
  pub_date = timezone.now()
  class Meta:
    model = Poll

◉ 构建策略

factory_boy 支持多种不同的构建策略。构建策略是确定数据模型生成状态的方法。在测试用例中,如果需要使用不同状态的目标对象,就会用到它们(LIST 14.28)。

LIST 14.28 支持的构建策略

# 返回没有save 的Poll 实例
poll = PollFactory.build()
# 返回已save 的Poll 实例
poll = PollFactory.create()
# 以字典形式返回生成Poll 实例时所用的属性
attributes = PollFactory.attributes()
# 返回所有属性桩代码化后的对象
stub = PollFactory.stub()

直接实例化 Factory 类时,其运行效果与 create() 相同(LIST 14.29)。

LIST 14.29 Factory 类的实例化

poll = PollFactory() #=> 与PollFactory.create() 效果相同

生成实例时可以通过传递关键字传值参数的方式覆盖原有属性值,而无需关心使用的构建策略(LIST 14.30)。

LIST 14.30 覆盖属性

poll = PollFactory.create(question_text='changed question')

◉ 延迟计算属性

虽然我们在定义 Factory 的时候已经添加了静态的属性,但有些属性(关联的数据模型、需要动态生成的属性等)需要在每次生成实例时进行设置。准备这类属性就需要用到 LazyAttribute 了(LIST 14.31)。

LIST 14.31 LazyAttribute 的示例

class UserFactory(factory.django.DjangoModelFactory):
  first_name = 'Beproud'
  last_name = 'Taro'
  email = factory.LazyAttribute(lambda a:
                  '{0}.{1}@example.com'.format(a.first_name,
                                 a.last_name).lower())
  class Meta:
    model = User
UserFactory().email #=> beproud.taro@example.com

◉ 序列

根据特定格式需要用到连续的属性值时,可以使用序列(Sequence)(LIST 14.32)。

LIST 14.32 Sequence

class UserFactory(factory.django.DjangoModelFactory):
  email = factory.Sequence(lambda n: 'person{0}@example.com'.format(n))
  class Meta:
    model = User
UserFactory().email   # => 'person0@example.com'
UserFactory().email   # => 'person1@example.com'

基本的内容到这里就了解清楚了。接下来我们看看如何用 factory_boy 解决 Django 中的配置器不好用的问题。

14.3.4 消除“不便使用默认配置器的情况”

◉ 解决“日期字段会变成特定日期”的问题

按照 LIST 14.33 所示定义 PollFactory 类,然后为测试用例的属性配上合适的日期,这样我们就能得到想要的数据模型了。

LIST 14.33 设置灵活的日期字段

class PollFactory(factory.django.DjangoModelFactory):
  question_text = 'factory question'
  # 配上与测试匹配的日期
  pub_date = timezone.now()
  class Meta:
    madel = Poll
# 还可以在生成实例时设置
poll = PollFactory.create(pub_date=timezone.now())

◉ 解决“配置器不容易维护”的问题

配置器不易维护的问题怎么解决呢? Django 的配置器在给模型添加列时,需要对配置器的所有数据进行修改,但如果使用 factory_boy,则只需要给 Factory 类添加列即可。

只需进行下述修改,所有通过 PollFactory 生成的 Poll 模型中就都添加了 user_name 列(LIST 14.34)。

LIST 14.34 添加列

class PollFactory(factory.django.DjangoModelFactory):
  question_text = 'factory question'
  # 添加的列
  user_name = 'factory san'
  pub_date = timezone.now()
  class Meta:
    model = Poll

通过上面的内容可以看出,Django 的配置器对于简单的测试、事先准备数据等用途而言十分便捷,但要想在实际开发中持续地实施测试,还是建议使用更加灵活且易于维护的 factory_boy。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文