解决 Django 功能测试中数据会被清除的问题

不久前,山姆锅开始学习 Django 这套 Web 应用框架,在不跳脱它既有有框架的情况下,运用它可以快速建构应用程序原型。差不多同一时间,也找到 Test-driven Development with Python 这本好书,书名虽然好像跟 Django 无关,但书中的范例程序是以 Django 为基础。 可惜,本文不是要示范如何使用 Django 做功能测试,这个主题网络有许多文章可以参考。山姆锅在使用 Django 的 LiveServerTestCase 时 发现数据库的数据在第一个测试案例 (test case) 之后就会被清除,导致后续的测试无法正常运行。

Django 的功能性测试 (或者称用户验收测试) 最常使用 Selenium 来驱动浏览器,并利用 django.test.LiveServerTestCase 在背景建立测试数据库以及 WSGI 服务器。 如此,完成一个端到端 (end-to-end) 的测试环境,基本上可以模拟大多数用户跟应用之间的交互情境。 这么完整的支持,原本是该多么理想啊! 本文的解决方法应该只适用 SQLite 3

执行环境

在说明山姆锅遇到的问题前,需要先列出我的测试环境:

  • Python 2.7.x
  • Django 1.8.3
  • SQLite 3
  • OS: Mac OS X

注意使用的是 SQLite 3 数据库。

问题描述

除了 LiveServerTestCase 支持功能测试外, Django 也对单元测试 (unit test) 提供有 django.test.TestCase 来协助撰写。 TestCase 利用数据库对于交易 (transaction) 的支持,在每次测试案例之后,还原数据库到案例开始前的状态,这样来达到案例之间的分隔 (isolation) 目的。 基于一个山姆锅还不了解的原因,毕竟山姆锅对于 Django 还没有那么多经验,LiveServerTestCase 不是使用数据库的交易来还原状态, 而是对数据库进行清除动作,就是这个神秘的清除动作,导致在功能测试时只有第一个执行的案例能够正常, 后续用到数据库的案例都会发生读不到数据的情况。

有人建议功能测试不要使用 SQLite 3,使用 MySQL 或者 PostgreSQL, 原因不外乎 SQLite 3 在功能测试不稳定等等。 可是山姆锅打算开发的应用就需要使用 SQLite 3 啊!所以,并没有针对 MySQL 或者 PostgreSQL 来测试过。

解决方法

山姆锅采用的解决方法是直接继承 TestCaseLiveServerTestCase 这两个类,继承 [TestCase` 使用数据库交易来还原数据库状态, 使用 LiveServerTestCase 来设定测试用的 WSGI 服务器。这个新的类很没有创意地叫做 FunctionalTest,源代码如下:

django_testcase.py view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

from __future__ import absolute_import, print_function, unicode_literals

import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings.test')

import time

from django.core import signals
from django.test.testcases import LiveServerTestCase, TestCase
from django.contrib.auth import get_user_model
from django.contrib.staticfiles.handlers import StaticFilesHandler


class (LiveServerTestCase, TestCase):
static_handler = StaticFilesHandler


def setUpClass(cls):
"""
Django's LiveServerTestCase setupClass but without sqlite :memory check
and with additional Ghost initialization.
"""
default_address = os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:8090-9000')
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = default_address

# as from Django 1.8.
try:
from django.db import close_connection
signals.request_finished.disconnect(close_connection)
except ImportError:
pass
super(FunctionalTest, cls).setUpClass()

@classmethod
def tearDownClass(cls):
"""
Django's LiveServerTestCase tearDownClass, but without sqlite :memory
check and shuts down the Ghost instance.
"""
super(FunctionalTest, cls).tearDownClass()

def setUp(self):
get_user_model().objects.create_superuser(
username='demo',
password='demo',
email='[email protected]'
)
super(FunctionalTest, self).setUp()

def tearDown(self):
super(FunctionalTest, self).tearDown()

def sleep(self, secs):
time.sleep(secs)

结语

Django LiveServerTestCase 的这个问题困扰山姆锅一阵子,网络上也没有太多有用的数据。 本文的解法是参考 Fast functional testing with Django and Ghostrunner 这篇文章。 必须老实讲,山姆锅不知道这个问题的确切原因,也不知道有没有其他解法,所以感觉真是不踏实。不管如何,希望对跟我遇到相同问题的人有所帮助。

参考数据

_`Django`: https://www.djangoproject.com/

_`Test-driven Development with Python`: http://shop.oreilly.com/product/0636920029533.do

_`Selenium`: https://selenium-python.readthedocs.org/

_`SQLite 3`: https://docs.python.org/2/library/sqlite3.html

_`Fast functional testing with Django and Ghostrunner`: https://wearespindle.com/articles/fast-functional-testing-with-django-and-ghostrunner/

_` 原始的 LiveServerTestCase 实践 `: https://github.com/wearespindle/ghostrunner/blob/master/ghost/test/testcases.py

原文引用 大专栏  https://www.dazhuanlan.com/2019/08/27/5d64b575185a2/


猜你喜欢

转载自www.cnblogs.com/petewell/p/11418215.html
今日推荐