Django Test(测试)

 对于当今Web developer来说,自动化测试是一项非常有用的”除虫”工具。你可以使用测试集——test suite——去避免或者解决一系列的问题:
    当你在写新代码的时候,测试可以验证你的代码是否按预期执行
    当你重构或则修改旧代码时,你可以使用测试来确保你的修改不会对程序造成不良影响

    测试Web 程序是一项复杂的工作。因为一个Web程序由多种逻辑层组成——从Http层的request 执行,到表单的确定和处理,到模板的渲染。利用Django测试框架以及齐全的配套工具,
    你可以模request,插入测试数据,检查程序输出和一般情况下的测试。

    最重要的是,Django测试框架非常简单!

    Django中,测试由两种方式:doctests 和unit tests。

    doctests:
        doctests使用的是python中标准的doctest测试模块。它是由系统在你的docstring(注释)中查找测试代码。

    Django 的test runner在你的models.py文件中查找并执行你的doctests。如果找到tests.py文件,它同样会去里面查找doctests。
          你可以将doctest语句放入你工程中的任何models.py文件中。但一般情况下,会将app的doctests放在module docstring中,而model的doctests会放在各自的model中

    举例:

        from django.db import model
        class Animal(models.Model):
            """
            An animal that knows how to make noise
            # Create some animals
            >>> lion = Animal.objects.create(name="lion", sound="roar")
            >>> cat = Animal.objects.create(name="cat", sound="meow")
            # Make 'em speak
            >>> lion.speak()
            'The lion says "roar"'
            >>> cat.speak()
            'The cat says "meow"'
            """
            name = models.CharField(maxlength=20)
            sound = models.CharField(maxlength=20)
            def speak(self):
                return 'The %s says "%s"' % (self.name, self.sound)

        当你运行测试的时候,test utility会找到这个docstring。这里需要注意一下,上面的docstring有
        部分类似于python的交互环境。好了,运行它吧。

    unittests:
        Django的unit tests使用的标准模块库是unittest。和doctests一样,Django的test runner 在

    models.py文件或者在app目录下存在的tests.py文件中查找定义了的unit test cases。

       一个与上例doctests等价的unittest test case如下:
        import unittest
        from myapp.models import Animal
        class AnimalTestCase(unittest.TestCase):
            def setUp(self):
               self.lion = Animal.objects.create(name="lion", sound="roar")
               self.cat = Animal.objects.create(name="cat", sound="meow")
            def testSpeaking(self):
                self.assertEquals(self.lion.speak(), 'The lion says "roar"')
               self.assertEquals(self.cat.speak(), 'The cat says "meow"')

        当你运行tests的时候,test utility会找到models.py 和tests.py文件中所有的test case,在这些
        test case外自动构造一个test suite,并运行它。

 
    我应该用哪种?
           选择哪一个测试框架经常是有争议的,所以Django同时支持python的两种标注测试框架。选择哪一
    个取决于程序员个人的爱好。Django对于两种测试框架给与相同的支持。因为每一种测试系统都有自身
    的优点,所以最好的方法是将两者结合使用,使用对你测试最合适的测试系统。
           对于一个程序测试新手,这种选择可能会令人迷惑,所以这里给出了两种测试系统的一些诧异,以
    帮助你选择一个适合你的。
           如果你已经使用Python一段时间,doctest可能你会觉得doctest更Python一些。它让写测试尽
    量简单,(so there’s no overhead of writing classes or methods不知道咋翻译;()你只是简单
    的把测试代码放入了docstrings中。这样有一个附加的好处就是同时给modules提供了一份documentation
    ——好的doctests可以兼顾test和documentation,一石二鸟。

           如果你刚开始学习测试,那么doctests会让你快速入门。
           Unittest会让从java转过来的程序员觉得熟悉。因为unittest就是从Junit处获得灵感的。如果你
    有使用过类似于Junit测试框架的经验,那unittest会让你更习惯些。
           Unittest是根据class和method组织的,如果你在为一些类似的代码写一系列测试,那么你可以使
    用它的子集去抽象出它的共同任务。这样可以使你的代码小巧而整洁。它也提供直接的安装和卸载程序,
    可以使你在一个更高的层面上控制你的test case 运行的环境。
           重申,你可以并行的使用两种测试系统,甚至在同一个app中。大部分的项目都会用到这两种测试
    系统。两个测试系统都能找到更适合自己的位置。

运行tests
    你可以使用下列语句运行你的测试
    $ ./manage.py test
    默认情况下,它会运行你项目里写在INSTALLED_APPS里所有app的测试. 如果你只想要测试其中某一个app,你比如说animals(假设你的INSTALLED_APPS里有两个app--myproject.polls和myproject.animals)    ,你可以运行以下
    命令来达到目的
    # ./manage.py test animals

    注意,我们使用了animals,而不是myproject.animals

    现在你可以(svn版本)针对更细力度的test class 或者 test method进行测试了.
    比如你的animals app 的测试单元里有AnimalTestCase测试类,这个类拥有testFluffyAnimals这个测试方法.那么,你可以通过
    $ ./manage.py test animals.AnimalTestCase
    来只运行AnimalTestCase这个测试类,通过
    $ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
    来运行testFluffyAnimals这个测试方法.

测试数据库
    测试运行需要的数据库并不需要你真实的数据库.一个单独的测试数据库会被创建.
    不管你的测试是否通过,当你所有的测试都执行过后,这个测试数据库就会被销毁.
    默认情况下,测试数据库的名字是test_DATABASE_NAME,DATABASE_NAME是你在settings.py里配置的数据库名.如果你需要给测试数据库一个其他的名字,在settings.py中指定TEST_DATABASE_NAME的值.
    除了使用一个单独的数据库外,测试工具会使用相同的数据库配置--DATABASE_ENGINE, DATABASE_USER, DATABASE_HOST等等.创建测试数据库的用户由DATABASE_USER(settings中)指定,所以你需要确认DATABASE_USER
    有足够的权限去创建数据库.

理解test输出的内容
    当你运行你的test后,你会看到一系列打印出来的信息.你可以在命令行中使用verbosity这个选项来控制输出信息的详细程度.

    Creating test database...
    Creating table myapp_animal
    Creating table myapp_mineral
    Loading 'initial_data' fixtures...
    No fixtures found.

    这段信息告诉你正在创建一个测试数据库.
    当创建测试数据库后,Django就会运行你的test.如果你的所有测试都通过了,你会看到以下信息,
    ----------------------------------------------------------------------
    Ran 22 tests in 0.221s

    OK
    
    如果有test没有通过,你会看到没有通过的test的细节:
    ======================================================================
    FAIL: Doctest: ellington.core.throttle.models
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/dev/django/test/doctest.py", line 2153, in runTest
        raise self.failureException(self.format_failure(new.getvalue()))
    AssertionError: Failed doctest test for myapp.models
      File "/dev/myapp/models.py", line 0, in models

    ----------------------------------------------------------------------
    File "/dev/myapp/models.py", line 14, in myapp.models
    Failed example:
        throttle.check("actor A", "action one", limit=2, hours=1)
    Expected:
        True
    Got:
        False

    ----------------------------------------------------------------------
    Ran 2 tests in 0.048s

    FAILED (failures=1)

    一份完整的错误信息输出超出了本文档的范围,但这会让你更直观;)你可以去查看python的unittest库了解更多细节.


测试工具

       Django提供了一系列小工具用来写测试.
Test Client
    test client是一个python类,来模拟一个简单的“哑”浏览器,允许你来测试你的view函数.
    你可以使用test client完成下列事情.
        模拟"Get"和"Post"请求,观察响应结果--从HTTP(headers,status codes)到页面内容.
        测试url执行了正确的view函数.
        测试一个request被Django模板渲染.

    Test Client无意于取代Twill,Selenium这些框架.Django有不同的专注点.

概略
    使用test client,需要引入django.test.client.Client
        >>> from django.test.client import Client
        >>> c = Client()
        >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
        >>> response.status_code
        200
        >>> response = c.get('/customer/details/')
        >>> response.content
        '
    
    如例所示,你需要实例化Client.

    test client运行机制
        test client并不需要有Web server.这是因为test client避开了HTTP的额外开销,直接处理Django框架.这样就可以是unit test运行飞快.
        
        当请求页面时需要指定url路径.不是整个域路径.
        这是对的
        >>> c.get('/login/')
        这是错的
        >>> c.get('http://www.example.com/login/')

        test client不适合操作不是由Django建立的网站.如果,你非要如此,请使用python的标准库--urllib或者urllib2.
        为了解析URl,test client使用由ROOT_URLCONF(settings.py)指定的URLconf
        尽管上例可以在python内置环境里运行,一些test client的功能,尤其是模板相关的功能,只有在test运行时才可以使用.


Making requests

    可以使用django.test.client.Client类来构造request.它在构造时并不需要参数.
        >>> c = Client()

    一个client实例可以调用下列方法:

    get(path, data={}):
         创建一个path的Get请求,并返回Response object.
    Data字典的key-value值会被用来创建一个由Get得到的数据.如:
        c = Client()
        c.get('/customers/details/', {'name':'fred', 'age':7})
    它与下述请求等价:
        http://yoursite.com/customers/details/?name=fred&age=7

    post(path, data={}, content_type=MULTIPART_CONTENT)
    创建一个path的POST请求,并返回Response object.
    Data字典的key-value值会被用来创建POST的数据.如:
        >>> c = Client()
        >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'})
    它会请求'/login/'这个url,并发送数据name=fred&passwd=secret    
    如果提供了content_type参数,data会被包含在POST请求里,以HTTP头Content-Type格式被发送.
    如果没有提供,data会以multipart/form-data的content_type格式被发送.这样的话,Data字典的key-value值会作为multipart message进行编码,并创建一个post数据载体.
    
    为一个参数提交多个多个值时--比如选住