Unit Testing

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

Django Unittest
Django Unittest

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

  1. 尋找django.test.TestCase的子類
  1. 建立用于測試的數據庫
  2. 尋找以 test開頭的方法
  3. 建立實例
  4. 檢測結果

Test Doubles

  1. Dummy Object: 需要傳遞但不會真正調用
  1. Fake Object: 是真正接口或抽象類的簡易實現
  2. Test Stub: 傳遞間接的輸入
  3. 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()函數:

  1. 獲取classA().get_value(x)的返回值
  1. 當返回值>10,打印x+'>10'到標準輸出
  2. 當返回值<=10,打印x+'<=10'到標準輸出

哪些需要mock:

  1. 標準輸出('sys.stdout')
    用 'StringIO' 假扮 'sys.stdout' 獲取間接輸出:
    @patch('sys.stdout', new_callable=StringIO)
  1. 間接輸入 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 測試概要

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容