Unit Test
- What 什么是單元測試
- Why 為什么要做單元測試
- How 怎么做單元測試
What is Unit Test
- 什么是單元測試
- 最小代碼塊,最基本功能
- 最少依賴,可獨立執行
- 易于執行,可重現,可自動化
- 什么不是單元測試:
- 訪問數據庫
- 訪問網絡(如 RESTful 服務接口)
- 訪問文件系統
- 不能獨立運行
- 運行單元測試需要額外的配置等
Why Unit Test
- 可持續的替代手動測試
- 保證基本功能達到預期
- 減少可預期的bug
- 有助于團隊協作
How to do Unit Test
Python 單元測試 unittest:
- test fixture 測試現場
- test case 測試用例
- test suite 測試集
- test runner 測試執行
- settings.TEST_RUNNER: django.test.simple.DjangoTestSuiteRunner
What's a Test Case
- Setup 建立測試現場
- Action 執行
- Assertion 斷言
- Teardown 清理測試現場
What's a Test Case

Code
<pre><code>
def check_email_available(request):
get_vars = request.GET
js = {'success': False}
if len(User.objects.filter(email=get_vars['email'])) > 0:
js['value'] = _("An account with the Email '{email}' already exists.").format(
email=get_vars['email'])
js['field'] = 'email'
return JsonResponse(js, status=200)
js['success'] = True
return JsonResponse(js, status=200)
</code></pre>
Unit Testing
<pre><code>
class CheckEmailTest(TestCase):
def setUp(self):
# Create the test client
self.client = Client()
self.url = reverse('student.views.check_email_available')
def test_email_available(self):
post_params = {'email': 'test8f9g@gmail.com'}
with patch('student.models.AUDIT_LOG') as mock_audit_log:
result = self.client.get(self.url, post_params)
# Assertion: Check response and log
self._assert_response(result, success=True)
def test_email_unavailable(self):
post_params = {'email': 'test8f9g@edx.org'}
with patch('student.models.AUDIT_LOG') as mock_audit_log:
result = self.client.get(self.url, post_params)
# Assertion: Check response and log
self._assert_response(result, success=False)
</code></pre>
<pre><code>
class LoginAjaxTest(TestCase):
def setUp(self):
self.email = 'bryantly@126.com'
self.client = Client()
self.url = reverse('login')
def test_login_success(self):
post_params = {'email': self.email, 'password':'12345678'}
with patch('student.models.AUDIT_LOG') as mock_log:
result = self.client.post(self.url, post_params)
self._assert_response(result, success=True)
def test_login_failure_password_blank(self):
post_params = {'email': self.email, 'password':''}
with patch('student.models.AUDIT_LOG') as mock_log:
result = self.client.post(self.url, post_params)
expectedDict = {'issue':'blank', 'field':'password'}
self._assert_response(result, success=False, **expectedDict)
</code></pre>
Unittest in Django
- 尋找django.test.TestCase的子類
- 建立用于測試的數據庫
- 尋找以 test開頭的方法
- 建立實例
- 檢測結果
Test Doubles
- Dummy Object: 需要傳遞但不會真正調用
- Fake Object: 是真正接口或抽象類的簡易實現
- Test Stub: 傳遞間接的輸入
- Mock Object: 記錄間接的輸出
Why Mock
- 真實對象的行為具有不確定性,例如網絡延遲
- 真實對象難以創建,例如和UI交互
- 真實對象的行為難以模擬,例如網絡錯誤
- 真實對象運行效率很低,讀寫數據庫等
- 真實對象還沒有實現
Mock in Python
Python mock 模塊
- MagicMock()
- patch(), patch.object(), patch.dict()
<pre><code>
from StringIO import StringIO
class classA(object):
def get_value(self, x):
# TODO: get value of x
def foo(x):
print '{}{}'.format(x, '>10' if classA().get_value(x) >10 else '<=10')
</code></pre><pre><code>
@patch('sys.stdout', new_callable=StringIO)
@patch('main.classA', new_callable=MagicMock)
def test_value_gt_10(mock_class, mock_stdout):
mock_class.return_value.get_value.return_value = 11
foo('fooCallable')
assert mock_stdout.getvalue() == 'fooCallable>10\n'
</code></pre><pre><code>
@patch('sys.stdout', new_callable=StringIO)
@patch('main.classA', new_callable=MagicMock)
def test_value_lt_10(mock_class, mock_stdout):
mock_class.return_value.get_value.return_value = 10
foo('foo')
assert mock_stdout.getvalue() == 'foo<=10\n'
</code></pre>
foo()函數:
- 獲取classA().get_value(x)的返回值
- 當返回值>10,打印x+'>10'到標準輸出
- 當返回值<=10,打印x+'<=10'到標準輸出
哪些需要mock:
- 標準輸出('sys.stdout')
用 'StringIO' 假扮 'sys.stdout' 獲取間接輸出:
@patch('sys.stdout', new_callable=StringIO)
- 間接輸入 classA.get_value(x)
用 'MagicMock' 假扮 '__main__.classA' 傳遞間接輸入:
Mock classA:
@patch('__main__.classA', new_callable=MagicMock)
Mock classA().get_value():
mock_class.return_value.get_value.return_value = 30
UnitTest in Edx
- Factory 的修改
- TestCase 的更新
- 自動化配置
相關閱讀
edx 測試概要
- FactoryBoy: 批量生成數據
- unittest: Python單元測試
- jasmine: JS單元測試
- Dummy HTTP server: 簡易的HTTP客戶端
- UI Acceptance Tests