【有书共读15】Python测试驱动开发 读书笔记04
第一个Django应用,第一个单元测试
Django 鼓励以应用的形式组织代码。
$ python manage.py startapp lists
这个命令会在 superlists 文件夹中创建子文件夹 lists,与 superlists 子文件夹相邻,并在 lists 中创建一些占位文件,用来保存模型、视图以及目前最关注的测试:
这个命令会在 superlists 文件夹中创建子文件夹 lists,与 superlists 子文件夹相邻,并在 lists 中创建一些占位文件,用来保存模型、视图以及目前最关注的测试:
superlists/
├── db.sqlite3
├── functional_tests.py
├── lists
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── superlists
├── __init__.py
├── __py***__
├── settings.py
├── urls.py
└── wsgi.py
├── db.sqlite3
├── functional_tests.py
├── lists
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── superlists
├── __init__.py
├── __py***__
├── settings.py
├── urls.py
└── wsgi.py
单元测试及其与功能测试的区别
单元测试和功能测试之间的界线有时不那么清晰。不过,二者之间有个基本区别:功能测试站在用户的角度从外部测试应用,单元测试则站在程序员的角度从内部测试应用。
采用的工作流程大致如下。
(1) 先写功能测试,从用户的角度描述应用的新功能。
(2) 功能测试失败后,想办法编写代码让它通过(或者说至少让当前失败的测试通过)。此时,使用一个或多个单元测试定义希望代码实现的效果,保证为应用中的每一行代码(至少)编写一个单元测试。
(3) 单元测试失败后,编写最少量的应用代码,刚好让单元测试通过。有时,要在第 2 步和第 3 步之间多次往复,直到我们觉得功能测试有一点进展为止。
(4) 然后,再次运行功能测试,看能否通过,或者有没有进展。这一步可能促使我们编写一些新的单元测试和代码等。
由此可以看出,这整个过程中,功能测试站在高层驱动开发,而单元测试则从低层驱动我们做些什么。
这个过程看起来是不是有点儿烦琐?有时确实如此,但功能测试和单元测试的目的不完全一样,而且最终写出的测试代码往往区别也很大。
(1) 先写功能测试,从用户的角度描述应用的新功能。
(2) 功能测试失败后,想办法编写代码让它通过(或者说至少让当前失败的测试通过)。此时,使用一个或多个单元测试定义希望代码实现的效果,保证为应用中的每一行代码(至少)编写一个单元测试。
(3) 单元测试失败后,编写最少量的应用代码,刚好让单元测试通过。有时,要在第 2 步和第 3 步之间多次往复,直到我们觉得功能测试有一点进展为止。
(4) 然后,再次运行功能测试,看能否通过,或者有没有进展。这一步可能促使我们编写一些新的单元测试和代码等。
由此可以看出,这整个过程中,功能测试站在高层驱动开发,而单元测试则从低层驱动我们做些什么。
这个过程看起来是不是有点儿烦琐?有时确实如此,但功能测试和单元测试的目的不完全一样,而且最终写出的测试代码往往区别也很大。
<h2color:> Django中的单元测试Django 建议我们使用 TestCase 的一个特殊版本。这个版本由 Django 提供,是标准版 unittest.TestCase 的增强版,添加了一些 Django 专用的功能,后面几章会介绍。</h2color:>
现在,要启动神奇的 Django 测试运行程序。和之前一样,这也是一个 manage.py 命令:
$ python manage.py test
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_bad_maths (lists.tests.SmokeTest)
---------------------------------------------------------------------
Traceback (most recent call last):
File "/.../superlists/lists/tests.py", line 6, in test_bad_maths
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
---------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
System check identified no issues (0 silenced).
Destroying test database for alias 'default'...
Creating test database for alias 'default'...
F
======================================================================
FAIL: test_bad_maths (lists.tests.SmokeTest)
---------------------------------------------------------------------
Traceback (most recent call last):
File "/.../superlists/lists/tests.py", line 6, in test_bad_maths
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
---------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
System check identified no issues (0 silenced).
Destroying test database for alias 'default'...
提交~~
$ git status # 会显示一个消息,说没跟踪lists/
$ git add lists
$ git diff --staged # 会显示将要提交的内容差异
$ git commit -m "Add app for lists, with deliberately failing unit test"
$ git add lists
$ git diff --staged # 会显示将要提交的内容差异
$ git commit -m "Add app for lists, with deliberately failing unit test"
(大伙儿要扎油呀~~)