Django单元测试

测试例子部分源于官方文档,部分为自己碰到的案例,官方文档链接:https://docs.djangoproject.com/en/2.2/topics/testing/

除了官方文档提到的内容,本文主要写一下自己使用过程中碰到的问题和解决方法

0X01 如何编写单元测试

Python有unittest库来进行单元测试,Django的单元测试是基于unittest库的,只不过在unittest的基础上进行了封装,并提供了一系列单元测试工具,来帮助你进行测试。Django在创建APP之后,默认会在APP目录下创建一个tests.py文件,这里就是存放你测试代码的地方,当然,如果需要测试的内容多了,都放在一个文件中显然不合理,所以Django提供高级玩法,模块化测试,这个后面再说,先上一个例子:

from django.test import TestCase
from myapp.models import Animal

class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

上面就是一个最基础的测试用例,你所编写的测试类需要继承于TestCase,当你运行测试时,Django会找到所有的以test开头的文件,运行这些文件中所有父类是TestCase的类中的测试方法,测试方法为以test开头的函数。

上面的例子中,setUp函数会在运行test_xxxx函数前被调用,可以进行一些初始化操作,例如创建测试数据,获取Token等。

0X02 运行单元测试

在Django项目目录下使用manage.py来运行单元测试,该命令会运行所有APP的所有tests.py中的所有测试:

python manage.py test

运行某个package下的所有测试:

python manage.py test package_name

运行某个package下的某个测试类(TestCase):

python manage.py test pack.tests.xxxTestCase

运行某个package下的某个测试类的某个测试方法:

python manage.py test pack.tests.xxxTestCase.test_animals_can_speak

上面提到过,默认会匹配所有以test_func开头的测试方法,你也可以自己制定测试方法的正则:

python manage.py test --pattern="tests_*.py"

在运行单元测试的时候,可以使用ctrl+c来结束测试,测试程序将等待当前正在运行的测试结束,然后正常退出。在退出期间,测试程序会和往常一样输出测试失败的详细信息、运行了多少测试、出现的错误和故障,并且会像往常一样销毁测试数据库。

连续按下两次ctrl+c将会强制退出测试程序,此时会缺少一些详细信息,也不会销毁测试数据库,慎用。

0X03 测试数据库

需要和model交互的话会需要用到数据库,但Django不会使用生产环境的数据库进行测试,会创建单独的测试数据库,例如settings.py中定义的连接的数据库为product_db,那么默认创建的测试数据库就为test_product_db。

上面提到过,在执行单元测试命令后,会自动创建测试数据库,结束测试后会自动销毁测试数据库。每次都去创建和销毁比较耗时,如果不需要销毁测试数据库可以携带keepdb参数,不销毁数据库:

python manage.py test --keepdb

如上一节所述,如果强制中断测试运行,则可能不会销毁测试数据库。在下一次运行时,将询问您是否要重用或销毁数据库。使用该选项可以禁止该提示并自动销毁数据库。例如,在连续集成服务器上运行测试时,这可能很有用,例如,测试可能会因超时而中断:

python manage.py test --noinput

默认Django settings中指定的是sqlite数据库,那么测试数据库将创建在内存中,不会使用文件系统。在settings.py中的DATABASES提供了很多关于测试数据库的选项,例如指定测试数据库的名字:、

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'USER': 'mydatabaseuser',
        'NAME': 'mydatabase',
        'TEST': {
            'NAME': 'mytestdatabase',
        },
    },
}

更多字段:https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-TEST_NAME

有一个地方暂时没有捋清楚什么意思,如果有兄弟知道可以分享下,原文:https://docs.djangoproject.com/en/2.2/ref/applications/#django.apps.AppConfig.ready

Finding data from your production database when running tests?

If your code attempts to access the database when its modules are compiled, this will occur before the test database is set up, with potentially unexpected results. For example, if you have a database query in module-level code and a real database exists, production data could pollute your tests. It is a bad idea to have such import-time database queries in your code anyway - rewrite your code so that it doesn’t do this.

This also applies to customized implementations of ready().

0X04 关于Model测试

涉及到Model的测试,有一个需要注意的地方,每个测试方法都是独立的,比如编写了以下测试类,在测试方法A创建的对象,用测试方法B去获取,是获取不到的,在测试方法A结束后该对象就被销毁了,具体例子:

class UserTestCase(TestCase):

    def setUp(self):
        UserInfo.objects.create(username='sup_user', password='123456')

    def test_add_user(self):
        UserInfo.objects.create(username='user', password='123456')

    def test_sup_user_exist(self):
        self.assertEqual(UserInfo.objects.filter(username='sup_user').count(), 1)

    def test_user_exist(self):
        self.assertEqual(UserInfo.objects.filter(username='user').count(), 1)

上述代码在setUp方法中创建了sup_user用户,在test_add_user方法中创建了user用户,然后test_sup_user_exist方法测试sup_user是否存在,test_user_exist方法测试user用户是否存在,测试结果如下:

可以看出来,setUp方法创建的对象生命周期是整个测试类的运行过程,test方法中的对象生命周期仅仅是这个方法。当然本类setUp创建的对象,在其他类也是查询不到的,数据库一直为空,实测得出的结论。

0X05 API的测试

Django提供了Client来进行API测试,https://docs.djangoproject.com/en/2.2/topics/testing/tools/

>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200

0X06 执行测试的顺序

1.首先执行所有TestCase的子类

2.接着执行SimpleTestCase和TransactionTestCase的子类

3.最后执行unittest.TestCase和doctests

0X07 rest_framework单元测试

实际场景中经常会用到rest_framework和rest_framework_jwt。那么所有的API都是需要进行身份验证的,那么该如何进行单元测试呢?官方文档:https://www.django-rest-framework.org/api-guide/testing/

1.APIRequestFactory

DjangoRequestFactory的子类,具体的参数和用法查阅文档即可,下面是一个DEMO:

from rest_framework.test import APIRequestFactory

class InterfaceTestCase(TestCase):

    def setUp(self):
        self.user = UserInfo.objects.create_user(username='sup_admin', password='123456')

    def test_list_method(self):
        factory = APIRequestFactory()
        request = factory.get('/interfaces/')
        view = VIEWSET_CONF['interface_list'] # viewset.as_view({'get': 'list'})
        force_authenticate(request, user=self.user)
        response = view(request)
        self.assertEqual(response.status_code, 200)

首先创建APIRequestFactory对象,调用get方法,返回一个WSGIRequest对象,实例化处理请求的视图函数对象并调用,返回response。可以看到在setUp方法里我创建一个用户,然后在test方法里使用force_authenticate进行强制身份验证,这样就可以正常访问视图了。

2.APIClient

扩展了Django的Client,DEMO:

from rest_framework.test import APIClient

class InterfaceTestCase(TestCase):

    def setUp(self):
        self.user = UserInfo.objects.create_user(username='sup_admin', password='123456')
        self.client = APIClient()
        self.client.force_authenticate(self.user)

    def test_list_method(self):
        response = self.client.get('/interfaces/')
        self.assertEqual(response.status_code, 200)

文档中提到了使用client.login方法来通过验证,但是该方法只支持SessionAuthntication,JWT的话不支持。

self.client.login(username=self.user.username, password=self.user.password)

3.RequestsClient

有别于上面提到的所有方法,该方法是完全模拟一个外部请求的请求过程,使用的是Python的requests库,这个使用起来注意事项很多,有需求自行查阅文档

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django是一个流行的Python Web框架,它提供了一套完整的开发工具和功能,包括单元测试单元测试是一种软件测试方法,用于验证代码的各个单元(函数、方法、类等)是否按照预期工作。 在Django中,你可以使用内置的测试框架来编写和运行单元测试。以下是关于Django单元测试的一些介绍: 1. 测试文件位置:通常,你可以在每个应用程序的tests.py文件中编写单元测试代码。这样可以将测试代码与应用程序代码分开,并且可以轻松地运行和管理测试。 2. 测试类和方法:你可以创建一个继承自django.test.TestCase的测试类,并在其中定义各种测试方法。每个测试方法应该以test_开头,并且可以使用各种断言方法来验证预期结果。 3. 测试数据库Django提供了一个特殊的测试数据库,用于在每次运行测试时进行数据隔离。这意味着你可以在测试中创建、修改和删除数据,而不会影响到开发或生产数据库。 4. 测试客户端:Django提供了一个测试客户端,用于模拟HTTP请求和响应。你可以使用该客户端来测试视图函数、URL路由和模板渲染等功能。 5. 运行测试:你可以使用Django的manage.py命令来运行单元测试。例如,可以使用"python manage.py test"命令来运行所有应用程序的测试,或者使用"python manage.py test app_name"命令来运行特定应用程序的测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值