doctest, unittest, nose, pytest

內建: n/a

標準:

  • doctest
  • unittest

第三方:

  • pytest
  • nose
  • nose2
  • 比較
    • google trend 分析結果顯示 nose 近來已被 pytest 超越. nose2 普及率仍不及前兩者
    • nose2 is not nose

Test Runner Wrapper:

  • python manage.py test (django 專案專用)
  • python setup.py test
    • 安裝 pytest or nose 後會自動擴充 setup.py 命令, 新增一個 test 命令
  • nosetests (由套件 nose 提供)
  • py.test (由套件 pytest 提供)
  • runtests.py (由套件 pytest 提供)

其他

  • unittest2 (舊版 python 專用)
  • testr (openstack 專案專用)

doctest

(1) 單元代碼, 測試腳本, test runner 都寫在 source.py

source.py

"""
>>> add(1, 1)
2
"""
def add(x, y):
    return x+y

if __name__ == '__main__':
    import doctest
    doctes.testmod()

run

python source.py -v

(2) 單元代碼, 測試腳本寫在 source.py (我比較喜歡 2-2 這種形式)

source.py (2-1)

"""
>>> add(1, 1)
2
"""
def add(x, y):
    return x+y

or (2-2)

def add(x, y):
    """
    >>> add(1, 1)
    2
    """
    return x+y

run

python -m doctest -v source.py

(3) 單元代碼寫在 source.py; 測試腳本寫在 test.txt

source.py

def add(x, y):
    return x+y

test.txt

>>> from source import add 
>>> add(1, 1)
2

run

python -m doctest -v test.txt

(4) 單元代碼, test runner 寫在 source.py; 測試腳本寫在 test.txt

source.py

def add(x, y):
    return x+y

if __name__ == '__main__':
    import doctest
    doctes.testfile('test.txt')

test.txt

>>> from source import add 
>>> add(1, 1)
2

run

python source.py -v

unittest

(1) 單元代碼, 測試腳本, test runner 都寫在 source.py

source.py

def add(x, y):
    return x+y

import unittest
class AddTestCase(unittest.TestCase):
    def test_add(self):
        self.aseertEqual(add(1, 1), 2)

if __name__ == '__main__':
    unittest.main()

run

python source.py -v

(2) 單元代碼, 測試腳本寫在 source.py

source.py

def add(x, y):
    return x+y

import unittest
class AddTestCase(unittest.TestCase):
    def test_add(self):
        self.aseertEqual(add(1, 1), 2)

run

python -m unittest -v source

(3) 單元代碼寫在 source.py; 測試腳本寫在 test.py (我比較喜歡這種形式)

source.py

def add(x, y):
    return x+y

test.py

from source import add
import unittest
class AddTestCase(unittest.TestCase):
    def test_add(self):
        self.aseertEqual(add(1, 1), 2)

run (3-1)

python -m unittest -v test # 3-1-1
python -m unittest -v test.AddTestCase # 3-1-2
python -m unittest -v test.AddTestCase.test_add # 3-1-3

or (3-2)

python -m unittest discover -v # discover 預設會搜尋並執行 test*.py

參考資料

重點摘要

  • 定義一個繼承 unittest.TestCase 的子類別 , 通常命名為 xxxxTestCase
  • 在自定義的子類別中撰寫一或多個 test_oooo(self) 測試方法 , 一個方法代表一個測試
  • 測試方法中如果需要資源 (測試設備) 的取得與釋放, 可以在特殊方法 setUp(self) 和 tearDown(self) 中處理
  • 為了確保 test isolation,每一個 test method 都是透過一個全新的 TestCase 來執行

pytest

  • py.test
  • runtests.py (deprecated since version 2.8)
  • python setup.py test

(1) 單元代碼, 測試腳本, test runner 都寫在 source.py

n/a

(2) 單元代碼, 測試腳本寫在 source.py

n/a

(3) 單元代碼寫在 source.py; 測試腳本寫在 test_add.py 或 add_test.py

source.py

def add(x, y):
    return x+y

test_add.py 或 add_test.py

from source import add
def test_add():
    assert add(1, 1) == 2

run (3-1)

py.test test_add.py

or (3-2)

py.test # 預設會搜尋並執行 test_*.py 或 *_test.py

python runtests.py

py.test --genscript=runtests.py
python runtests.py

python setup.py test

# setup.py
from setuptools import setup
setup(
    setup_requires=['pytest-runner',],
    tests_require=['pytest',], 
)

# setup.cfg
[aliases]
test=pytest

python setup.py test

前三十大 Python 開源專案

排名 項目 nose pytest 說明
- google-api-python-client Y - unittest2,mock
- horizon - - manage.py test
- python-novaclient - - testr
1 django - - -
2 requests - Y -
3 httpie - Y -
4 flask - Y -
5 tornado - - unittest2,mock
6 ansible Y - unittest2,mock
8 sentry - - mock
9 scrapy - Y mock
13 ipython Y - -
17 salt - - -

Workshop: Test-Driven Web Development with Django

http://test-driven-django-development.readthedocs.io/en/latest/index.html

Test-Driven Django Tutorial

http://www.tdd-django-tutorial.com/

Faster Django Testing

http://tech.marksblogg.com/faster-django-testing.html

Changing Django cache backend between test cases

http://www.2general.com/blog/2012/08/09/changing_django_cache_backend_between_test_cases.html


Python Mocking 101: Fake It Before You Make It

https://blog.fugue.co/2016-02-11-python-mocking-101.html

An Introduction to Mocking in Python

https://www.toptal.com/python/an-introduction-to-mocking-in-python

Small Testing Guide

https://wiki.openstack.org/wiki/SmallTestingGuide


Django 內建的測試框架繼承自 Python 內建的 unittest 模組

參考資料

重點摘要

  • from django.test import TestCase 或 from unittest import TestCase
    • If your tests rely on database access such as creating or querying models, be sure to create your test classes as subclasses of django.test.TestCase rather than unittest.TestCase.
    • Using unittest.TestCase avoids the cost of running each test in a transaction and flushing the database, but if your tests interact with the database their behavior will vary based on the order that the test runner executes them. This can lead to unit tests that pass when run in isolation but fail when run in a suite.

results matching ""

    No results matching ""