TTD(测试驱动):开发环境第一个测试

TDD

  • 测试驱动开发:首先编写测试

  • 每次只前进一步

开始项目

  • 创建项目目录,新建functional_tests.py

from selenium import webdriver
browser = webdriver.Firefox()
browser.get('http://localhost:8000')
assert 'Django' in browser.title

尝试执行

python3 functional_tests.py

显示错误

FileNotFoundError: [Errno 2] No such file or directory: 'geckodriver'

看来缺少了FireFox的驱动核心gecko,参考 How to install firefoxdriver webdriver for python3 selenium on ubuntu 16.04?:

  • 高于FireFox 47的Firefox使用新型驱动,有两种解决方法:

    • 下载和降级到Firefox 46

    • 下载最新的Marionette驱动并修改代码适应新环境

参考 Installing Selenium and ChromeDriver on Ubuntu 以及 Marionette > WebDriver

cd ~/bin
tar xfz geckodriver-v0.14.0-macos.tar.gz
  • 然后配置

export PATH=$PATH:/path/to/geckodriver

此时再次执行 python3 functional_tests.py 就可以看到打开了本地firefox浏览器访问测试页面 http://localhost:8000/。当然,此时我们还没有运行Django,所以看到的是空白错误页面。

运行Django

django-admin.py startproject superlists

上述django-admin.py命令会创建一个superlists文件夹并创建一些结构文件。现在我们开始运行Django:

cd superlists
python3 manage.py runserver

这里有一个提示

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

尝试 python manage.py migrate 则提示

Traceback (most recent call last):
  File "manage.py", line 17, in <module>
    "Couldn't import Django. Are you sure it's installed and "
ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?

这个PYTHONPATH环境问题参考 Django installed, but can't import django in python

执行命令检查路径

$ python3
>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']

需要执行类似

$ PYTHONPATH=/path/to/django/parent/dir python
>>> import django  # should work now

实际操作可以使用

PTTHONPATH=/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django python3

建议使用 virtualenv 来构建环境。 不过上述错误实际上是我忘记python2没有安装django,需要使用python3

再次运行 python3 functional_tests.py 就可以看到Django正常工作的提示界面:

创建Git仓库

在阶段性完成工作后,需要将代码提交到版本控制系统。

将开发的第一个python代码移动到 superlists 目录下,然后开始初始化软件仓库

$ ls
functional_tests.py    superlists
$ mv functional_tests.py superlists/
$ cd superlists/
$ ls
db.sqlite3        manage.py
functional_tests.py    superlists
$ git init .
Initialized empty Git repository in /Users/huatai/Dropbox/works/tdd-python/superlists/.git/

目录中有一个db.sqlite3是数据库文件,这个文件不用纳入版本控制,所以将这个文件加入到.gitignore,告诉git将其忽略:

$ echo "db.sqlite3" >> .gitignore

接下来添加当前文件夹.中其他内容:

$ git add .
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   .gitignore
    new file:   functional_tests.py
    new file:   manage.py
    new file:   superlists/__init__.py
    new file:   superlists/__pycache__/__init__.cpython-36.pyc
    new file:   superlists/__pycache__/settings.cpython-36.pyc
    new file:   superlists/__pycache__/urls.cpython-36.pyc
    new file:   superlists/__pycache__/wsgi.cpython-36.pyc
    new file:   superlists/settings.py
    new file:   superlists/urls.py
    new file:   superlists/wsgi.py

此时发现我们错误添加了很多.pyc文件,这些文件没有必要提交。所以将这些文件从git中删除,并添加到.gitignore中:

$ git rm -r --cached superlists/__pycache__
rm 'superlists/__pycache__/__init__.cpython-36.pyc'
rm 'superlists/__pycache__/settings.cpython-36.pyc'
rm 'superlists/__pycache__/urls.cpython-36.pyc'
rm 'superlists/__pycache__/wsgi.cpython-36.pyc'
$ echo "__pycache__" >> .gitignore
$ echo "*.pyc" >> .gitignore

现在再次使用git status检查状态,没有问题我们就可以提交软件仓库了

git commit

第一个测试

#!/usr/bin/env python3

from selenium import webdriver
import unittest

class NewVisistorTest(unittest.TestCase):
    def setUp(self):
        self.browser = webdriver.Firefox()

    def tearDown(self):
        self.browser.quit()

    def test_can_start_a_list_and_retrieve_it_later(self):
        self.browser.get('http://localhost:8000')
        self.assertIn('To-Do', self.browser.title)
        self.fail('finish the test!')

if __name__ == '__main__':
    unittest.main(warnings='ignore')
  • 设置程序运行使用系统中python3

#!/usr/bin/env python3
  • 测试代码test_can_start_a_list_and_retrieve_it_later,所有以test_开头的方法都是测试方法,由测试主程序运行。类中可以定义多个测试方法。

  • setUptearDown是特殊方法,分别在测试方法之前和之后运行,这里使用两个方法打开和关闭浏览器。这两个方法类似try/except依据,如果测试出错,也会运行tearDown方法,这样测试结束,Firefox窗口就会关闭。

如果setUp方法抛出异常,则tearDown方法不会运行

  • unittest提供了很多用于编写测试断言的辅助函数,如assertEqualassertTrueassertFalse等,详细参考 http://docs.python.org/3/library/unittest.html

  • if __name__ == '__main__ 是用于检查python程序是否在命令行运行,而不是其他脚本中导入。调用unittest.main()启动unittest测试运行程序会自动在文件中查找测试类和方法,然后运行。

  • warnings='ignore'会禁止抛出ResourceWarning异常。

执行以上测试,会看到启动Firefox,并输出测试包报告,显示了测试失败的内容

$ ./functional_tests.py
F
======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisistorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./functional_tests.py", line 15, in test_can_start_a_list_and_retrieve_it_later
    self.assertIn('To-Do', self.browser.title)
AssertionError: 'To-Do' not found in 'Welcome to Django'

----------------------------------------------------------------------
Ran 1 test in 3.165s

FAILED (failures=1)

很好,我们现在知道访问的浏览器标题窗口没有To-Do关键字

  • Selenium测试中还经常使用等待页面加载的指令implicitly_wait,以便Selenium能等待页面完全加载。

    def setUp(self):
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(3)

implicitly_wait不适合所有情况,复杂测试需要显式等待

参考

Last updated