- 引言
- 本书涉及的内容
- 第 1 部分 Python 开发入门
- 第 1 章 Python 入门
- 第 2 章 开发 Web 应用
- 第 3 章 Python 项目的结构与包的创建
- 第 4 章 面向团队开发的工具
- 第 5 章 项目管理与审查
- 第 6 章 用 Mercurial 管理源码
- 第 7 章 完备文档的基础
- 第 8 章 模块分割设计与单元测试
- 第 9 章 Python 封装及其运用
- 第 10 章 用 Jenkins 持续集成
- 第 11 章 环境搭建与部署的自动化
- 第 12 章 应用的性能改善
- 第 13 章 让测试为我们服务
- 第 14 章 轻松使用 Django
- 第 15 章 方便好用的 Python 模块
- 附录 A VirtualBox 的设置
- 附录 B OS(Ubuntu)的设置
14.3 fixture replacement
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论