有时候在本地开发api的时候,这个时候并没有搭建好开发环境,没有部署到机器上面,但是 API 在这个时候也是需要测试的,至少得保证功能的完善可用,这种情况下,可以使用 Django 的 单元测试来解决在未部署运行 WEB 的情况下,测试其功能。

Django单元测试基础

  1. 在应用目录下新建tests.py文件
  2. test.py导入TestCase类: from django.test import TestCase
  3. 编写测试用例,继承TestCase类,编写以test_为前缀的测试方法,参数无
  4. 通过self.Assert_系列函数检查结果是否符合预期

简单单元测试

Django 里面集成了 python 的单元测试,可以在里面测试基本的方法和类,当然我们所关心的 ORM 也是在里面 可以测试的。

测试常见基本方法:

### 需要测试的方法
def validate_hash(device_hash):
    """
    Check if device hash is valid

    :param device_hash: string
    :return bool: true if valid
    """
    lent = len(str(device_hash).split('-'))
    if lent != 4 or len(str(device_hash)) != 19:
        return False
    for ch in str(device_hash):
        if ch >= 'A' and ch <= 'G' or ch >= '0' and ch <= '9' or ch == '-':
            continue
        else:
            return False
    return True

### 测试样例
...
def test_validate_device_hash(self):

    device_hash = [
        ("AAAA-BBB0-FFFF-GGGG", True),
        ("AAAA", False),
        ("AAA0-5555-9GTF-2222", False),
        ("DDDD-2222-CCCC-AAAA", True),
    ]

    for hash, expected in device_hash:
        self.assertEquals(validate_hash(hash), expected)

ORM测试样例

对于 ORM 的测试主要是功能和数据格式的测试,相对来说并不是那么容易,但是 django 的 restframework 里面包含了 对于数据格式的 serializer 可以合在一起使用。

  • 导入from rest_framework import serializers到测试类中
  • 编写serializer
  • 编写测试用例测试

简单示例:

from django.db import models

class Device(models.Model):
    device_hash = models.CharField(max_length=19, blank=False, null=False)
    service_id = models.IntegerField()


from rest_framework import serializers
from rest_framework.exceptions import ValidationError

### 检测hash是否合法
def validate_hash(device_hash):
    """
    Check if device hash is valid

    :param device_hash: string
    :return bool: true if valid
    """
    lent = len(str(device_hash).split('-'))
    if lent != 4 or len(str(device_hash)) != 19:
        return False
    for ch in str(device_hash):
        if ch >= 'A' and ch <= 'G' or ch >= '0' and ch <= '9' or ch == '-':
            continue
        else:
            return False
    return True

### 序列化检查函数
class ReturnSerializer(serializers.Serializer):
    service_id = serializers.CharField(max_length=512, required=True)
    device_hash = serializers.CharField(max_length=19, required=True)

    def validate(self, attrs):
        if not validate_hash(attrs["device_hash"]):
            raise serializers.ValidateError("device hash is not valid")
        if not attrs["service_id"] in (5002, 5015):
            raise serializers.ValidateError("service id is unknown")
        return attrs

from django.test import TestCase
### 测试用例
class DeviceViewTestCase(TestCase):
    """测试设备字段"""

    def test_get_device(self):
        """
        测试获取设备信息
        
        :raise error: assert error
        """
        devices = Device.objects.filter().values("device_hash", "service_id")

        serializer = ReturnSerializer(data=devices, many=True)
        try:
            serializer.is_valid(raise_exception=True)
        except ValidationError as e:
            self.assertTrue(False, str(e))

此示例用来检查多个返回值是否合法,可以用在协商式交互过程中,用来校验外来 API 请求数据是不是合法,或者是 检查自己返回的信息是不是符合预期。

API测试方法

Django 作为一个web框架,自然API测试是测试重点,文章开头提到了在未部署 web 的情况下,如何测试 api 是不是合法? 本文结合 rest_framework 和 django 给出简单的示例,希望对开发有帮助。

简单示例:


import json
from django.test import TestCase, RequestFactory


from views import DeviceView

class DeviceViewTestCase(TestCase):
    """test device view class"""

    def setUp(self):
        self.factory = RequestFactory()

    def test_create_device(self):
        """test post function"""

        data = [
            {
                "device_hash": "AAA3-DDDD-FF35-FACD",
                "service_id": 1052
            },
            {
                "device_hash": "AAA3-DDDD-FF55-FACD",
                "service_id": 1052
            }
        ]

        req = self.factory.post(
            "api/nuri/wechat/device/",
            data=json.dumps(data),
            content_type="application/json"
        )
        resp = DeviceView.as_view()(req).render()
        self.assertEqual(resp.data["status"], 2000)

需要认证的API测试

有时候有些api需要先登录然后才能测试,这种情况下,需要使用 session 来认证后,再来请求其余的 API

简单示例:

import requests
from django.test import TestCase

login_url = "http://127.0.0.1/api/krosa/auth/login/"
HTTP_HOST = "www.aliyun.com"

class DeviceAuthTestCase(TestCase):
    """Test The Device Authentic API"""

    def setUp(self):
        """login in setup"""
        self.client = requests.session()
        self.client.headers.update({"Host": HTTP_HOST})
        response = self.client.post(login_url, json={
            "login_name": login_user,
            "password": login_pass,
            "type": 128
        })
        if response.status_code != 200:
            raise LoginError("login http error code != 200")
        # update x-xsrf-token
        XSRF_TOKEN = self.client.cookies["XSRF-TOKEN"]
        self.client.headers.update({"x-xsrf-token": XSRF_TOKEN})
    
    def test_get_user_info(self):
        pass

完~