Django单元测试基础知识

相关资源

Django官方文档
unittest文档

简单的例子

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

# Django的单元测试基于unittest库
class StudentTestCase(TestCase):
    # 测试函数执行前执行
    def setUp(self):
        print("======in setUp")

    # 需要测试的内容
    def test_add(self):
        student = Student(name='aaa')
        student.save()
        self.assertEqual(student.name, 'aaa')
    
    # 需要测试的内容
    def test_check_exit(self):
        self.assertEqual(0,  Student.objects.count())

     # 测试函数执行后执行
    def tearDown(self):
        print("======in tearDown")

关于django的单元测试,需要知道的是

  1. 对于每一个测试方法都会将setUp()和tearDown()方法执行一遍
  2. 会单独新建一个测试数据库来进行数据库的操作方面的测试,默认在测试完成后销毁。
  3. 在测试方法中对数据库进行增删操作,最后都会被清除。也就是说,在test_add中插入的数据,在test_add测试结束后插入的数据会被清除。
  4. django单元测试时为了模拟生产环境,会修改settings中的变量,例如, 把DEBUG变量修改为True, 把ALLOWED_HOSTS修改为[*]。

运行单元测试

在单元测试中,可以指定测试粒度。这样可以专注于只测试还没测试的单元测试,而已经测试过的就不测试了。

# 测试整一个工程
$ ./manage.py test 

# 只测试某个应用
$ ./manage.py test app --keepdb


# 只测试一个Case
$ ./manage.py test animals.tests.StudentTestCase

# 只测试一个方法
$ ./manage.py test animals.tests.StudentTestCase.test_add

一些常见问题的解决

数据表多时创建数据库销毁过多时间

在单元测试时,若migrations的文档过多时,每次单元测试时间绝大部分都消耗在数据库的创建。试过,单元测试代码运行只要几十秒,而数据库的创建却用去了十分钟。这是个让人绝望的速度,万幸的是django有提供命令使用进行单元测试过后不删除数据库。 这个命令就是: --keepdb

指定测试数据库的默认字符集

在创建测试数据库时,数据库的默认字符集也许不是我们想要的例如latin1。可以通过在数据库配置中指定TEST_CHARSET, TEST_COLLATION 参数,来指定字符集以及排序规则

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'xxxx',
        'USER': 'xxxx',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',
            'init_command': 'SET default_storage_engine=INNODB',
        },
        'TEST_CHARSET': 'utf8',
        'TEST_COLLATION': 'utf8_general_ci',
    },

}

** settings变量的修改**

若干需要在单元测试时修改,setting命令。例如,django在单元测试时会将settings.DEBUG 设置为True, 而我们需要将其设置为False

方式1: 直接在修改

class BaseApiTest(TestCase):
    def setUp(self):
        #testcase DEBUG = False
        settings.DEBUG = False
        
    def test_b(self):
        self.assertEqual(2, 1+1)
    
    def tearDown(self):
        pass

方式2: 通过装饰器修改

from django.test.utils import override_settings
    
class BaseTest(TestCase):   
    def setUp(self):
        pass
    
    # 利用该装饰器,可以在但个测试函数内修改settings变量, 而不影响
    @override_settings(DEBUG=False)
    def test_b(self):
        self.assertEqual(2, 1+1)
    
    def tearDown(self):
        pass

API权限问题的解决

在测试API的时候,往往需要等等进行用户登录才有权限调用,此时需要指定登录用户来解决接口调用的权限问题

# 如果是API使用了rest_framework框架
from rest_framework.test import APIClient
class BaseTest(TestCase):   
    def setUp(self):
        
        # 创建一个用户
        self.user = create_user(uuid4().hex, '123456789')
        self.client = APIClient()
        
        # 通过force_authenticate函数来执行用户
        self.client.force_authenticate(self.user)

    
    def test_b(self):
        self.assertEqual(2, 1+1)
    
    def tearDown(self):
        pass


Celery异步任务的测试

在代码中几乎肯定是会有celery异步任务,若想对异步任务进行单元测试。可以将CELERY_ALWAYS_EAGER=True, BROKER_BACKEND='memory'

from xxx.celery import app


@app.task(bind=True)
def add(self,x, y):
    return x + y

class TaskTest(TestCase):
    def setUp(self):
        settings.CELERY_ALWAYS_EAGER = True
        
    def test_add(self):
        self.assertEqual(2, add.apply_async((1,1)))
    
    def tearDown(self):
        pass

单元的等级(来自知乎的gashero)

在知乎上看到gashero根据经验总结出来的单元测试总结,非常认同。根据功能的重要性,来进行不同程度的测试。

  • Level1:正常流程可用,即一个函数在输入正确的参数时,会有正确的输出
  • Level2:异常流程可抛出逻辑异常,即输入参数有误时,不能抛出系统异常,而是用自己定义的逻辑异常通知上层调用代码其错误之处
  • Level3:极端情况和边界数据可用,对输入参数的边界情况也要单独测试,确保输出是正确有效的
  • Level4:所有分支、循环的逻辑走通,不能有任何流程是测试不到的
  • Level5:输出数据的所有字段验证,对有复杂数据结构的输出,确保每个字段都是正确的地方

小小的感想

单元测试是需要时间,若要把各种情况都测试一遍,也许单元测试的编写时间要比写代码的时间还要长。目前由于时间关系,比较少写单元测试,但我很是期望能挤出时间尽量的编程单元测试。对于我来说,单元测试存在的意义就是,可以让我放心的重构代码,可以在重构代码的时候省下测试重构的代码能否正确运行的时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,597评论 6 427
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,010评论 19 139
  • GitHub 上有一个 Awesome - XXX 系列的资源整理,资源非常丰富,涉及面非常广。awesome-p...
    若与阅读 18,791评论 4 418
  • 环境管理管理Python版本和环境的工具。p–非常简单的交互式python版本管理工具。pyenv–简单的Pyth...
    MrHamster阅读 3,856评论 1 61
  • 假如明天就是世界末日,你今天会怎样度过? 假如这世间人都死去了,你会怎样面对孤独? —— 9.23
    岚风的叶子阅读 298评论 0 0