开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
Django 1.6 的测试驱动开发 - 技术翻译 - 开源中国社区

Django 1.6 的测试驱动开发 【已翻译100%】

标签: Django
renwofei423 推荐于 4年前 (共 14 段, 翻译完成于 01-21) 评论 13
收藏  
134
推荐标签: Django 待读

测试驱动开发(TDD)是一个迭代的开发周期,强调编写实际代码之前编写自动化测试。

这个过程很简单:

  1. 先编写测试。

  2. 查看测试失败的地方

  3. 编写足够的代码以使测试通过。

  4. 再次测试。

  5. 代码重构 。

  6. 重复以上操作。

tdd-process

目录

这是一个长的帖子,为了您的方便所以提供了目录:

  1. 什么是TDD

  2. 第一次测试

  3. 安装Django

  4. 功能测试

  • 管理员登录

  • 设置联系人应用程序

    5.单位测试

  • 主界面

  • 所有联系人视图

  • 添加联系人视图

  • 视图

  • 验证

  • 创建联系人

    6.功能测试完回家

    7.结构测试

    8,总结

砖家
 翻译得不错哦!

为什么要用TDD?

使用TDD,你将学会把你的代码拆分成符合逻辑的,简单易懂的片段,这有助于确保代码的正确性。

这一点非常重要,因为做到下面这些事情是非常困难的:

  1. 在我们的脑中一次性处理所有复杂的问题。

  2. 了解何时从哪里开始着手解决问题。

  3. 在代码库的复杂度不断增长的同时不引入错误和bug;并且

  4. 辨别出代码在什么时候发生了问题。

TDD帮助我们定位问题。它不能保证你的代码完全没有错误;然而,你可以写出更好的代码,从而能更好地理解理解代码。这本身有助于消除错误,并且至少,你可以更容易的定位错误。

TTD实际上也是一种行业标准。

说的够多了。让我们来看看代码吧。

在这个教程里,我们将创建一个存储用户联系人的app。

请注意: 这篇教程假设你运行在一个基于Unix的环境里 - 例如, Mac OSX, Linux, 或者在Windows下的Linux VM。 我将使用Sublime 2作为文本编辑器。并且,确保你已经完成了官方的Django教程并且基本了解Python语言. 此外,在这个第一篇post里,我们不会涉及到Django1.6提供的新工具。这篇文章将为之后的post打好基础来处理不同形式的测试。

BoydWang
 翻译得不错哦!

第一个测试

在开始做一些事情之前,我们需要首先创建一个测试。为了这个测试,我们需要让Django正确安装。为此我们将使用一个函数测试——这在下面会详细解释。

  1. 创建一个新目录存放你的项目:

    $ mkdir django-tdd
    $ cd django-tdd

  2. 再建立一个目录存放函数测试

    $ mkdir ft
    $ cd ft

  3. 创建一个新文件 "tests.py"并加入以下代码:

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

  4. 现在运行测试:

    $ python tests.py

    确认安装selenium(译注:自动化测试软件)时是使用 installed -pip安装的

    你将看到 FireFox弹出来试图打开 http://localhost:8000/。在你的终端上面你会看到:

    Traceback (most recent call last):File "tests.py", line 7, in <module>assert 'Django' in body.textAssertionError

    祝贺!你完成了第一个失效测试。

    现在我们写足够的代码来让它通过,这些代码量约相当于设置一个 Django 开发环境。

super0555
 翻译得不错哦!

设置Django

1. 激活一个virtualenv:

$ cd ..
$ virtualenv --no-site-packages env
$ source env/bin/activate

2. 安装Django并且建立一个项目

$ pip install django==1.6.1$ django-admin.py startproject contacts

你当前的项目结构应该是下面这个样子:

├── contacts
│   ├── contacts
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   └── manage.py
└── ft    
    └── tests.py

3. 安装 Selenium:

pip install selenium==2.39.0

4. 运行server

$ cd contacts
$ python manage.py runserver

5. 接着,打开一个新终端窗口,定位到"ft"文件夹下,再运行一次测试:

$ python tests.py

你将看到FireFox又一次窗口导航到了http://localhost:8000/。这次应该没有错误了。你刚刚已经通过了你的第一个测试!现在,让我们完成环境设置。

6. 版本控制,首先添加一个".gitignore"并且在里面添加下面的代码:

.Pythonenv
bin
lib
include.DS_Store.pyc

现在来创建一个Git仓库然后提交吧

$ git init
$ git add .$ git commit -am "initial"

7. 项目建完了,现在我们回头讨论一下功能测试吧。

BoydWang
 翻译得不错哦!

功能测试

我们通过用 Selenium 来进行第一次测试。这样的测试会使我们使用web浏览器就像我们是最终用户一样,来看看应用程序实际上是怎么运行的。因为这些测试是遵循最终用户的行为习惯——也可以说是用户用例——这个包含了对一系列产品特点进行测试,而不仅仅对单一功能进行测试——这种更适合单元测试。有一点非常需要注意的是,当这部分测试代码你还没开始写,那么你必须先从功能测试开始。由于我们基本上是测试Django的代码,所以功能测试是一个正确的方法去做的。

另一种方式去思考功能测试和单元测试的区别,就是功能测试主要关注在应用程序的外部,从用户的角度来进行测试,而单元测试主要是关注在应用程序的内部,从开发的角度进行测试。

在实践中会更多地体现这个概念。

在继续下个话题之前,我们先来重构我们的测试环境,使得测试起来更加简单。

  1. 首先,我们要重写在“tests.py”文件内的第一个测试:

    from selenium import webdriverfrom selenium.webdriver.common.keys import Keysfrom django.test import LiveServerTestCaseclass AdminTest(LiveServerTestCase):
    
      def setUp(self):
          self.browser = webdriver.Firefox()
    
      def tearDown(self):
          self.browser.quit()
    
      def test_admin_site(self):    
          # user opens web browser, navigates to admin page
          self.browser.get(self.live_server_url + '/admin/')
          body = self.browser.find_element_by_tag_name('body')
          self.assertIn('Django administration', body.text)

  2. 然后运行它:

    $ python manage.py test ft

    它会通过:

    ----------------------------------------------------------------------Ran 1 test in 3.304sOK

    恭喜你!

    在继续之前,我们先看看这里是怎么回事。如果所有都通过了,你也会看到FireFox浏览器被打开,然后按照我们在测试里所用的setUp()和tearDown()方法设置的功能进行整个过程。这个测试本身只是简单的测试这个"/admin" (self.browser.get(self.live_server_url + '/admin/')页面是否被找到,"Django administration"这个单词是否出现在body标签内。

    让我们确认一下。

  3. 运行服务:

    $ python manage.py runserver

    在地址栏里敲上地址 http://localhost:8000/admin/ 你会看到:

    admin-page

  4. 我们可以只需对错误的东西进行简单地测试便能确认测试是否正确运作。更新测试里的最后一行:

    self.assertIn('administration Django', body.text)

    重新再运行一次。你会发现有以下的错误(当然是我们所期望的):

    AssertionError: 'administration Django' not found in u'Django administration\nUsername:\nPassword:\n '

    修正测试,再测试一遍,就可以提交代码了。

    最后,你有没有注意到,我们用来进行实际测试的功能名称均以test_开头。这是为了让Django测试运行器能找到这些测试。换句话来说,任何一个以test_开头命名的功能都会被测试运行器视为一个测试。

轩骐
 翻译得不错哦!

管理员登陆

接下来,让我们来测试,以确保用户可以登录到管理网站。

  1. 更新“tests.py”文件中的test_admin_site功能:

    def test_admin_site(self):    
      # user opens web browser, navigates to admin page
      self.browser.get(self.live_server_url + '/admin/')
      body = self.browser.find_element_by_tag_name('body')
      self.assertIn('Django administration', body.text)
      # users types in username and passwords and presses enter
      username_field = self.browser.find_element_by_name('username')
      username_field.send_keys('admin')
      password_field = self.browser.find_element_by_name('password')
      password_field.send_keys('admin')
      password_field.send_keys(Keys.RETURN)
      # login credentials are correct, and the user is redirected to the main admin page
      body = self.browser.find_element_by_tag_name('body')
      self.assertIn('Site administration', body.text)

    所以 -

  • find_element_by_name- 是用于定位输入框。

  • send_keys- 发送键盘按键信息。

  1. 运行测试,你会发现这个错误:

    AssertionError: 'Site administration' not found in u'Django administration\nPlease enter the correct username and password for a staff account. Note that both fields may be case-sensitive.\nUsername:\nPassword:\n '

    这个之所以会失败,是因为我们没有管理员用户设置。这是一个预期中的失败,所以出现这种情况是对的。换句话来说,我们知道它会失败的,这使得我们更容易去解决它。

  2. 同步数据库:

    $ python manage.py syncdb

    设置一个管理员用户。

    再重新测试一遍。它依旧会失败。为什么呢?因为Django在运行的时候会给我们数据库创建一份副本,这样的测试方式不会影响生产数据库。

  3. 我们需要设置一个Fixture,是一个包含了我们想加载到测试数据库的数据文件:登录凭据。为了要实现这一点,当运行以下命令时,能够将数据库管理员用户信息从数据库转存到Fixture中去:

    $ mkdir ft/fixtures
    $ python manage.py dumpdata auth.User --indent=2 > ft/fixtures/admin.json

    现在更新AdminTest类:

    class AdminTest(LiveServerTestCase):
    
        # load fixtures
      fixtures = ['admin.json']
    
      def setUp(self):
          self.browser = webdriver.Firefox()
    
      def tearDown(self):
          self.browser.quit()
    
      def test_admin_site(self):    
          # user opens web browser, navigates to admin page
          self.browser.get(self.live_server_url + '/admin/')
          body = self.browser.find_element_by_tag_name('body')
          self.assertIn('Django administration', body.text)
          # users types in username and passwords and presses enter
          username_field = self.browser.find_element_by_name('username')
          username_field.send_keys('admin')
          password_field = self.browser.find_element_by_name('password')
          password_field.send_keys('admin')
          password_field.send_keys(Keys.RETURN)
          # login credentials are correct, and the user is redirected to the main admin page
          body = self.browser.find_element_by_tag_name('body')
          self.assertIn('Site administration', body.text)

    运行这个测试,它会通过。

    每次运行测试的时候,Django都会转存测试数据库。而这所有的Fixture都会在“test.py”文件中被指定加载到数据库中去。

  4. 让我们加一个或多个断言。再次更新测试:

    def test_admin_site(self):    
        # user opens web browser, navigates to admin page
        self.browser.get(self.live_server_url + '/admin/')
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Django administration', body.text)
        # users types in username and passwords and presses enter
        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys('admin')
        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys('admin')
        password_field.send_keys(Keys.RETURN)
        # login credentials are correct, and the user is redirected to the main admin page
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)
        # user clicks on the Users link
        user_link = self.browser.find_elements_by_link_text('Users')
        user_link[0].click()
        # user verifies that user live@forever.com is present
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('live@forever.com', body.text)

  5. 运行它,它会失败,因为我们需要添加另一个用户到fixture文件中:

    [{"pk": 1, "model": "auth.user", "fields": {
      "username": "admin", 
      "first_name": "", 
      "last_name": "", 
      "is_active": true, 
      "is_superuser": true, 
      "is_staff": true, 
      "last_login": "2013-12-29T03:49:13.545Z", 
      "groups": [], 
      "user_permissions": [], 
      "password": "pbkdf2_sha256$12000$VtsgwjQ1BZ6u$zwnG+5E5cl8zOnghahArLHiMC6wGk06HXrlAijFFpSA=", 
      "email": "ad@min.com", 
      "date_joined": "2013-12-29T03:49:13.545Z"}},{"pk": 2, "model": "auth.user", "fields": {
      "username": "live", 
      "first_name": "", 
      "last_name": "", 
      "is_active": true, 
      "is_superuser": false, 
      "is_staff": false, 
      "last_login": "2013-12-29T03:49:13.545Z", 
      "groups": [], 
      "user_permissions": [], 
      "password": "pbkdf2_sha256$12000$VtsgwjQ1BZ6u$zwnG+5E5cl8zOnghahArLHiMC6wGk06HXrlAijFFpSA=", 
      "email": "live@forever.com", 
      "date_joined": "2013-12-29T03:49:13.545Z"}}]

再次运行,它是会通过的。如果需要可以重构一下这个测试。现在想想还有什么可以测试。或许你可以测试管理员用户可以添加一个用户到管理面板中,或者可以测试没有管理员权限的人是不能进入管理面板中。写几个测试,更新你的代码,再次测试,根据需要重构代码。

接下来,我们会添加增加联系人应用,不要忘了提交代码哦!

轩骐
 翻译得不错哦!

设置联系人应用

  1. 开始一个测试,添加以下功能:

    def test_create_contact_admin(self):    
      self.browser.get(self.live_server_url + '/admin/')
      username_field = self.browser.find_element_by_name('username')
      username_field.send_keys('admin')
      password_field = self.browser.find_element_by_name('password')
      password_field.send_keys('admin')
      password_field.send_keys(Keys.RETURN)
      # user verifies that user_contacts is present
      body = self.browser.find_element_by_tag_name('body')
      self.assertIn('User_Contacts', body.text)
  2. 再次运行测试,你会看到以下错误:

    AssertionError: 'User_Contacts' not found in u'Django administration\nWelcome, admin. Change password / Log out\nSite administration\nAuth\nGroups\nAdd\nChange\nUsers\nAdd\nChange\nRecent Actions\nMy Actions\nNone available'

    这是预料之中的。

    现在,我们要写足够的代码让它通过。

  3. 新建一个应用:

    $ python manage.py startapp user_contacts
  4. 添加到“settings.py”文件:

    INSTALLED_APPS = (
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
      'ft',
      'user_contacts',)
  5. 在user_contacts目录下的“admin.py”文件中添加以下代码:

    from user_contacts.models import Person, Phonefrom django.contrib import admin
    
    admin.site.register(Person)admin.site.register(Phone)
  6. 你的工程架构会跟如下类似:

    .├── user_contacts
     │   ├── __init__.py
     │   ├── admin.py   
     │   ├── models.py
     │   ├── tests.py
     │   └── views.py
     ├── contacts
     │   ├── __init__.py
     │   ├── settings.py
     │   ├── urls.py
     │   └── wsgi.py
     ├── ft
     │   ├── __init__.py
     │   ├── fixtures
     │   │   └── admin.json
     │   └── tests.py
     └── manage.py
    
  7. 更新“models.py”:

    from django.db import modelsclass Person(models.Model):
      first_name = models.CharField(max_length = 30)
      last_name = models.CharField(max_length = 30)
      email = models.EmailField(null = True, blank = True)
      address = models.TextField(null = True, blank = True)
      city = models.CharField(max_length = 15, null = True,blank = True)
      state = models.CharField(max_length = 15, null = True, blank = True)
      country = models.CharField(max_length = 15, null = True, blank = True)
    
      def __unicode__(self):
          return self.last_name +", "+ self.first_nameclass Phone(models.Model):
      person = models.ForeignKey('Person')
      number = models.CharField(max_length=10)
    
      def __unicode__(self):
          return self.number
  8. 再次运行测试,你会看到:

    Ran 2 tests in 11.730sOK
  9. 我们继续下一步骤,添加测试进去以保证管理员可以添加数据:

    # user clicks on the Persons link
    persons_links = self.browser.find_elements_by_link_text('Persons')
    persons_links[0].click()
    # user clicks on the Add person link
    add_person_link = self.browser.find_element_by_link_text('Add person')
    add_person_link.click()
    # user fills out the form
    self.browser.find_element_by_name('first_name').send_keys("Michael")
    self.browser.find_element_by_name('last_name').send_keys("Herman")
    self.browser.find_element_by_name('email').send_keys("michael@realpython.com")
    self.browser.find_element_by_name('address').send_keys("2227 Lexington Ave")
    self.browser.find_element_by_name('city').send_keys("San Francisco")
    self.browser.find_element_by_name('state').send_keys("CA")
    self.browser.find_element_by_name('country').send_keys("United States")
    # user clicks the save button
    self.browser.find_element_by_css_selector("input[value='Save']").click()
    # the Person has been added
    body = self.browser.find_element_by_tag_name('body')
    self.assertIn('Herman, Michael', body.text)
    # user returns to the main admin screen
    home_link = self.browser.find_element_by_link_text('Home')
    home_link.click()
    # user clicks on the Phones link
    persons_links = self.browser.find_elements_by_link_text('Phones')
    persons_links[0].click()
    # user clicks on the Add phone link
    add_person_link = self.browser.find_element_by_link_text('Add phone')
    add_person_link.click()
    # user finds the person in the drop
    downel = self.browser.find_element_by_name("person")
    for option in el.find_elements_by_tag_name('option'):
      if option.text == 'Herman, Michael':
          option.click()
    # user adds the phone numbers
    self.browser.find_element_by_name('number').send_keys("4158888888")
    # user clicks the save button
    self.browser.find_element_by_css_selector("input[value='Save']").click()
    # the Phone has been added
    body = self.browser.find_element_by_tag_name('body')
    self.assertIn('4158888888', body.text)
    # user logs out
    self.browser.find_element_by_link_text('Log out').click()
    body = self.browser.find_element_by_tag_name('body')
    self.assertIn('Thanks for spending some quality time with the Web site today.', body.text)

这就是管理员的功能。让我们转过头来专注于user_contacts本身。你之前的代码还记得提交吗?如果没有,赶紧提交吧!

轩骐
 翻译得不错哦!

单元测试

考虑下我们现在已经写的特性。我们已经定义了我们的模型,允许管理员更改模型。根据这个情况和我们项目的整体目标,着重关注剩下的用户功能。

用户应该可以——

  1. 浏览所有的联系人。

  2. 添加新的联系人。

根据这些需求,尝试把剩下的功能测试公式化。尽管,在我们写功能测试之前,我们应该通过单元测试定义代码的行为——这有助于你写出良好、干净的代码,编写功能测试更加简单。

记住:功能测试最终将表示你的项目是否工作,而单元测试有助于你达到这样的目的。这很快就会变的有意义。

让我们暂停片刻,谈论一些常规惯例。

尽管TDD(或者终端)的基础——测试、代码、重构——是通用的,很多开发者使用的方法是不同的。例如,我喜欢先写单元测试,保证我们的代码在细粒度级别有效,然后写功能测试。其他开发者先写功能测试,查看它们失败,然后写单元测试,查看它们失败,然后再写代码,首先满足单元测试,最终也应该满足功能测试。这里没有正确和错误的答案。哪种方法舒服用哪种——但继续先测试、然后写代码,最后重构。

Ley
 翻译得不错哦!

视图

首先,检查所有视图都设置准确。

主视图

  1. 跟往常一样,先开始一个测试:

    from django.template.loader import render_to_stringfrom django.test import TestCase, Clientfrom user_contacts.models import Person, Phonefrom user_contacts.views import *class ViewTest(TestCase):def setUp(self):
        self.client_stub = Client()def test_view_home_route(self):
        response = self.client_stub.get('/')
        self.assertEquals(response.status_code, 200)

  2. 给这个测试文件取名为test_views.py,并保存到user_contacts/tests目录下。同时要添加__init__.py文件到目录中去,在user_contacts主目录下删除"tests.py"文件。

  3. 运行它:

    $ python manage.py test user_contacts

    它会失败的 -AssertionError: 404 != 200- 因为URL、视图和模板都还没存在。如果你不熟悉Django如何处理MVC架构,请点击这里阅览这篇简短的文章。我们首先获取用客户端获取url的“/”地址,这事Django的TestCase的一部分。这个响应被存储起来,然后我们去检查返回的状态码是否等于200。

  4. 添加如下路径到“contacts/urls.py”:

    url(r'^', include('user_contacts.urls')),

  5. 更新“contacts/urls.py”:

    from django.conf.urls import patterns, urlfrom user_contacts.views import *urlpatterns = patterns('',
        url(r'^$', home),)

  6. 更新“views.py”:

    from django.http import HttpResponse, HttpResponseRedirectfrom django.shortcuts import render_to_response, renderfrom django.template import RequestContextfrom user_contacts.models import Phone, Person# from user_contacts.new_contact_form import ContactFormdef home(request):
      return render_to_response('index.html')

  7. 添加“index.html”模板到模板目录中去:

    <!DOCTYPE html><html>
      <head>
        <title>Welcome.</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen">
        <style>
            .container {
                padding: 50px;
            }
        </style>
      </head>
      <body>
        <div class="container">
            <h1>What would you like to do?</h1>
            <ul>
                <li><a href="/all">View Contacts</a></li>
                <li><a href="/add">Add Contact</a></li>
            </ul>
        <div>
        <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
        <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
      </body></html>

  8. 再次运行测试,它就会顺利通过。

轩骐
 翻译得不错哦!

所有联系人视图

对这个视图的测试几乎和我们上一个测试相同。在看我的答案之前先自己试试吧。

1.通过在ViewTest类里添加下面的方法来开始这个测试。

def test_view_contacts_route(self):
  response = self.client_stub.get('/all/')
  self.assertEquals(response.status_code, 200)

2. 在运行时,你将看到同样的错误:AssertionError: 404 != 200

3. 用下面的路由策略更新"user_contacts/urls.py":

url(r'^all/$', all_contacts),

4. 更新"view.py":

def all_contacts(request):
  contacts = Phone.objects.all()
  return render_to_response('all.html', {'contacts':contacts})

5. 在templates文件夹里加入一个叫"all.html"的模板:

<!DOCTYPE html><html><head><title>All Contacts.</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen"><style>
  .container {
    padding: 50px;
  }</style></head><body><div class="container">
  <h1>All Contacts</h1>
  <table border="1" cellpadding="5">
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Address</th>
      <th>City</th>
      <th>State</th>
      <th>Country</th>
      <th>Phone Number</th>
      <th>Email</th>
    </tr>
    {% for contact in contacts %}      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
      </tr>
    {% endfor %}  </table>
  <br>
  <a href="/">Return Home</a></div><script src="http://code.jquery.com/jquery-1.10.2.min.js"></script><script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script></body></html>

6. 然后测试应该能通过了。

增加联系人视图

这个测试与前面两个稍有不同,所以一定要仔细的跟着下列步骤走。

1. 在test suite里加入测试:

def test_add_contact_route(self):
  response = self.client_stub.get('/add/')
  self.assertEqual(response.status_code, 200)

2. 你将在运行时看到这样的错误:AssertionError: 404 != 200

3. 更新"urls.py":

url(r'^add/$', add),

4. 更新"views.py"

def add(request):person_form = ContactForm()return render(request, 'add.html', {'person_form' : person_form}, context_instance = RequestContext(request))

确保加入了如下的引用:

from user_contacts.new_contact_form import ContactForm

5. 创建一个叫 new_contact_form.py的新文件然后加入如下代码:

import refrom django import formsfrom django.core.exceptions import ValidationErrorfrom user_contacts.models import Person, Phoneclass ContactForm(forms.Form):
  first_name = forms.CharField(max_length=30)
  last_name = forms.CharField(max_length=30)
  email = forms.EmailField(required=False)
  address = forms.CharField(widget=forms.Textarea, required=False)
  city = forms.CharField(required=False)
  state = forms.CharField(required=False)
  country = forms.CharField(required=False)
  number = forms.CharField(max_length=10)

  def save(self):
      if self.is_valid():
          data = self.cleaned_data
          person = Person.objects.create(first_name=data.get('first_name'), last_name=data.get('last_name'),
              email=data.get('email'), address=data.get('address'), city=data.get('city'), state=data.get('state'),
              country=data.get('country'))
          phone = Phone.objects.create(person=person, number=data.get('number'))
          return phone

6. 加入"add.html"到模板文件夹里:

import refrom django import formsfrom django.core.exceptions import ValidationErrorfrom user_contacts.models import Person, Phoneclass ContactForm(forms.Form):
  first_name = forms.CharField(max_length=30)
  last_name = forms.CharField(max_length=30)
  email = forms.EmailField(required=False)
  address = forms.CharField(widget=forms.Textarea, required=False)
  city = forms.CharField(required=False)
  state = forms.CharField(required=False)
  country = forms.CharField(required=False)
  number = forms.CharField(max_length=10)

  def save(self):
      if self.is_valid():
          data = self.cleaned_data
          person = Person.objects.create(first_name=data.get('first_name'), last_name=data.get('last_name'),
              email=data.get('email'), address=data.get('address'), city=data.get('city'), state=data.get('state'),
              country=data.get('country'))
          phone = Phone.objects.create(person=person, number=data.get('number'))
          return phone

7. 是不是通过了?应该是的。如果没有,再检查一下。

BoydWang
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(13)
Ctrl/CMD+Enter

好长一篇文章,翻译辛苦了!
先顶一下再看~~~
尼玛 文章太长来 看不下去啊 555~~~
感觉牛逼了
测试驱动开发,我喜欢~~~
我去,翻下来看评论都翻了好久
TDD好像说了好多年了,但是在实际工作中还没遇到过一个走这个模式的项目。不知道看这篇文章的朋友有谁有实际切身地体会过改模式的
从来没有TDD成功过...
开发的时间都不够 哪有时间测试啊。。。
不喜欢selenium。。
mark

引用来自“sjt”的评论

测试驱动开发,我喜欢~~~

呵呵,你确定你真的喜欢?
用django-nose搭建自动化测试,也是比较方便的,它既支持单元测试也支持系统测试。
http://giftbrandmaker.com/myblog/?p=369
顶部