django 單元測試小結


測試的場景

框架Django1.8 測試工具 unittest, 要記得給test設置一個獨特的settings。

  1. 測試請求 也就是測試整個view部分 官方案例 其中可能會遇到登錄,或者時session怎么模擬的問題

  2. 測試帶有orm的模塊

  3. 需要mock的測試,比較多的情況是有第三方API調用, 發郵件,發短信這種

unittest提供的斷言種類挺多,但是經常用的也就幾個 self.assertContainsself.assertEqualself.assertTrue

順便提下有用的選項(我這里是單獨給測試寫了一個settings), 為了提高測試速度,可以把用不到的中間件,installed_apps之類的多余配置給去掉。

測試全部用例
python manage.py test --setting settings_test 測試某個APP python manage.py test appname --setting settings_test 測試某個app下的TeseCase類 python manage.py test alarm.tests.ModelTestCase --setting settings_test -v {1,2,3} 數字越大,顯示的輸出越詳細,測試的日志信息 python manage.py test --setting settings_test -v3 其他的選項請查看 --help python manage.py test --help
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

用請求測試 views函數

DJANGO中提供了Client類來模擬http請求,可以模擬不同的method,然后就是請求參數的模擬,用起來很方面。

#coding:utf-8 from django.test import TestCase, Client from sendviews import * from core.tests import create_user class SendviewsTestCase(TestCase): def setUp(self): self.user = create_user() self.device = Device(hostname="CN-BJ-0000-00", mac="ff:ff:ff:ff:ff:ff", user=self.user).save() def test_creat_sms(self): c = Client() rep = c.post("/acquireportal/createsms",{"phone": "13988902345", "ssid": "erya", "dmac": "ff:ff:ff:ff:ff:ff"}) # 測試http請求的返回碼是否正確 self.assertEqual(rep.status_code, 200) # 測試response的內容是否包含字符串 self.assertContains(rep, "OK") # 測試response的內容是否包含字符串 方法二 self.assertTrue('OK' in rep.content)
  • 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
  • 操作 session,例如用戶登錄,特殊的session值
from django.test import Client def init_client(user): client = Client() client.login(username=user.username, password="lzz") s = client.session s['cur_user_id'] = user.id s.save() return client
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 增加header
from django.test.utils import setup_test_environment setup_test_environment() from django.test.client import Client c = Client() # get 請求,帶參數,並增加header c.get('/some/path/', {'qs_param':'foo'}, **{'HTTP_USER_AGENT':'silly-human', 'REMOTE_ADDR':'127.0.0.1'}) #get 請求,沒有帶參數,自定義headers c.get('/some/path/', **{'HTTP_USER_AGENT':'silly-human', 'REMOTE_ADDR':'127.0.0.1'})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 使用 RequestFactory 對象來進行測試,不是從 http client來發起,某些情況會用到
from django.test import TestCase, RequestFactory from django.http import HttpResponse from util.sign import generate_sign, validate_sign from util.decorators import apiauth_required, SIGN_KEY @apiauth_required() def simpleapi(request): return HttpResponse('ok') class DecoratorsTestCase(TestCase): def setUp(self): self.factory = RequestFactory() def test_apiauth(self): # create request object key = SIGN_KEY query_string = {u"name": u"lzz", u"age": u"20", u"data": u"[python, java, golang, lua]"} token = generate_sign(query_string, key) query_string.update({u"sign": token}) req = self.factory.post("/api/test", data=query_string) response = simpleapi(req) self.assertEqual(response.status_code, 200) 
  • 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
  • HTML 文本測試,使用 constants 來判斷並不是個好的選擇,可以用render之后的字符串對比。

對於需要登陸的view,有client也比較容易操作,還有一些特殊的session的檢測等, 我這里做了一個簡單的封裝

from django.test import Client def init_client(user): client = Client() client.login(username=user.username, password="lzz") s = client.session s['cur_user_id'] = user.id s.save() return client
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

帶有mock的測試

對模塊中的方法mock或者是對一個對象中的方法進行mock。真對測試函數中一些無法直接測試的函數設置默認的返回值, py3標准庫中已經有了mock模塊,py2需要自己安裝, 推薦教程 使用Pyhton Mock進行單元測試1。 下面是個實際的代碼片段。

import mock from django.test import TestCase from core.models import Tenant from alarm.models import * from .controler import TenantAlarm class ModelTestCase(TestCase): def setUp(self): self.tenant = Tenant.objects.create(domainname="erya", comname=u"爾雅") @mock.patch.object(TenantAlarm, "sendAlarm") def test_record_alarm(self, mock_method): # record_alarm 這個中會調用sendAlarm方法 mock_method.return_value = None content = "ccccc" atype = 0 rec_uid = 0 Alarm().record_alarm(content=content, atype=0, rec_tid=self.tenant.id) class TenantAlarmTestCase(TestCase): def setUp(self): self.tenant = Tenant.objects.create(domainname="erya", comname=u"爾雅") @mock.patch.object(TenantAlarm, "sendSMS", return_value=None) @mock.patch.object(TenantAlarm, "sendEmail", return_value=None) def test_send_alarm(self, method1, method2): content = u"報警了" ta = TenantAlarm(self.tenant.id, content, {u'SMS': 0, u'EMAIL': 0}) ta.sendAlarm() @mock.patch('util.sendsms_com.send', return_value=1) def test_sendsms(self, send): ta = TenantAlarm(self.tenant.id, self.content, {u'SMS': 0, u'EMAIL': 0}) ta.sendSMS() self.assertEqual(0, Account.objects.get(tenant=self.tenant).sms_num) self.account.sms_num = 100 self.account.save() ta.sendSMS() self.assertEqual(99, Account.objects.get(tenant=self.tenant).sms_num)
  • 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

coverage

coverage是一個檢查單元測試覆蓋率的工具,django的文檔中也有簡要的說明coverage的集成 文檔地址

#測試並收集測試信息 coverage run --source='.' manage.py test --setting mandela.settings_test #查看測試結果 coverage report -m Name Stmts Miss Cover Missing ---------------------------------------------------------------------------------------- acquireportal/__init__.py 0 0 100% acquireportal/controler.py 65 47 28% 22-56, 60-71, 76-79 acquireportal/migrations/0001_initial.py 6 0 100% acquireportal/migrations/0002_auto_20160622_1059.py 6 0 100% acquireportal/migrations/0003_auto_20160622_1100.py 5 0 100% .... ---------------------------------------------------------------------------------------- TOTAL 8013 5858 27%
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

覆蓋率挺低的��

其他

測試 Exception, 被測試的代碼有拋異常的情況,單元測試中需要觸發這個異常來測試。

又個函數中可能會出現 queryset get 返回多個結果

from django.test import TestCase from django.core.exceptions import MultipleObjectsReturned class ControlerTest(TestCase): def setUp(self): self.user = User(username='test', email='lebing.zhou@cc.com', password='asdfadf') self.user.save() self.mac_addr = "ff:ff:ff:ff:ff:ff" self.mac = Mac(mac=self.mac_addr, user=self.user) self.mac.save() self.plan = BasePlan(name="包月", plan_type=1, month_price=19.9, goods=200) self.plan.save() def test_do_action3(self): self.mac2 = Mac(mac="ff:ff:ff:ff:ff:11", user=self.user) self.mac2.save() order = Order.objects.create_order(self.user, self.plan, 1, 1, "ff:ff:ff:ff:ff:fd") with self.assertRaises(MultipleObjectsReturned): do_action(order) #在出現這種異常的情況下,進行后面的邊界測試 self.assertNotEqual(Mac.objects.get(mac=self.mac_addr).timeleft, 200)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM