[Zope3-dev] Mail delivery failed: returning message to sender

Mail Delivery System Mailer-Daemon at python.org
Tue Feb 17 07:56:05 EST 2004


This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  corey at streamreel.net
    SMTP error from remote mailer after RCPT TO:<corey at streamreel.net>:
    host iris2.directnic.com [204.251.10.82]: 550 5.7.1 No such recipient

------ This is a copy of the message, including all the headers. ------

Return-path: <zope3-dev at zope.org>
Received: from cache1.zope.org ([12.155.117.38])
	by mail.python.org with esmtp (Exim 4.22)
	id 1At4iI-0007VT-Kx; Tue, 17 Feb 2004 07:52:34 -0500
From: zope3-dev at zope.org (srichter)
Reply-To: zope3-dev at zope.org
To: ;
Subject: [FunctionalTests] (new) first draft (generated from LaTeX file)
Message-ID: <20040217075234EST at dev.zope.org>
X-BeenThere: zope3-dev at zope.org
X-Zwiki-Version: 0.21.1
Precedence: bulk
List-Id:  <zope3-dev at zope.org>
List-Post: <mailto:zope3-dev at zope.org>
List-Subscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/FunctionalTests/subscribeform>
List-Unsubscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/FunctionalTests/subscribeform>
List-Archive: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/FunctionalTests>
List-Help: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture>
Date: Tue, 17 Feb 2004 07:52:34 -0500
X-Spam-Status: OK (default 0.000)

Writing Functional Tests

  Status

    IsDraft

  Authors

    StephanRichter

  Difficulty

    Newcomer Level  

  Skills

    - It would be nice to knwo how Zope 3 generated forms work.
      Optional.  


  Problem/Task

    Unit tests cover a large part of the testing requirements listed in
    the eXtreme Programming literature, but are not everything. There are
    also regression and functional tests. While regression tests can be
    handled with unit and doc tests, functional tests can't. For this reason
    the Zope 3 community members developed an extension to 'unittest' 
    that handle functional tests. This package is introduced in this recipe.
     


  Recipe

    
  Introduction


    Unit tests are very good for testing the functionality of a
    particular object in absence of the environment it will eventually live
    in. Regression tests build on this by testing the behavior of an object
    in a limited environment. Then functional tests should test the behavior
    of an object in a fully running system. Therefore functional tests often
    check the user interface behavior and it is not surprising that they are
    often found in the browser code of Zope. In fact, in Zope 3's
    implementation of functional tests there exists a base test case class
    for each view type, such as 'zope.testing.functional.BrowserTestCase' 
    or 'zope.app.dav.ftests.dav.DAVTestCase' .

    Each custom functional test case class provides some valuable
    methods that help you write the tests fast and efficiently. Here are the
    methods that the 'BrowserTestCase' provides.

    -'getRootFolder()' 
Returns the root folder of the database. This method is available in
      every functional test case class.

    -'makeRequest(path='', basic=None, form=None, env=' 
This class creates a new 'BrowserRequest' instance that can be used
      for publishing.  

      -'path' - This is the absolute path of the URL (i.e. the URL
        minus the protocol, server and port).

      -'basic' - It provides the authentication information of the
        format '"<login>:<password>"' . When Zope 3 is brought up
        for functional testing, a user with the login "mgr" and the password
        "mgrpw" is automatically created having the role "zope.Manager"
        assigned to it. So usually you will use '"mgr:mgrpw"' as
        your basic argument.

      -'form' - The argument is a dictionary that contains all
        fields that would be provided by an HTML form. Note that you should
        have covnerted the data already to their native Python format; be
        sure to only use unicode for strings.

      -'env' - This variable is also a dicationary where you can
        specify further environment variables, like HTTP headers. For
        example, the header 'X-Header: value' would be an entry of
        the form ''HTTP' in the dictionary.

      -'outstream' - Optionally you can define the the stream to
        which the outputted HTML is sent. If you do not specify one, one will
        be created for you.  


      However, one would often not use this method directly, since it
      does not actually publish the request. Use the 'publish()' 
      method described below.

    -'publish(self, path, basic=None, form=None, env=' 
The method creates a request, that is then published in a completely
      functional Zope 3 and finally returns a regular response object that is
      enhanced by a couple of methods:

      -'getOutput()' - Returns all of the text that was pushed to
        the outstream.

      -'getBody()' - Only returns all of the HTML of the response.
        It therefore excludes HTTP headers.

      -'getPath()' - Returns the path that was passed to the
        request.


      The 'path' , 'basic' , 'form' and 'env' have the same semantics
      as the equally-named arguments to 'makeRequest()' . If 'handle' 
      is 'False' , then occuring exceptions are not caught. If 'True' 
      , the default view of an exception is used and a nice formatted HTML
      page will be returned.

    -'checkForBrokenLinks(body, path, basic=None)' 
Given an output body and a published path, check whether the contained
      HTML contains any links and check that these links work. Since the
      availability of pages and therefore links depends on the permissions of
      the user, one might want to specify a login/password pair in basic. For
      example, if you have published a request as a manager, it will be very
      likely that the returned HTML contains links that require the manager
      role.


    
  Testing "Templated Page" Views


    Okay, now that we know how the 'BrowserTestCase' extends the normal
    'unittest.TestCase' , we can use it to write some functional tests
    for the "add", "edit" and "index" view of the "Templated Page" content
    type.

    Anywhere, create a file called 'test' and add the following
    functional testing code::

      01 import time
      02 import unittest
      04 from transaction import get_transaction
      05 from zope.testing.functional import BrowserTestCase
      06 from zope.app.content.zpt import ZPTPage
      08 class TemplatedPageTests(BrowserTestCase):
      09     """Funcional tests for Templated Page."""
      11     template = u'''
      12     <html>
      13       <body>
      14         <h1 tal:content="modules/time/asctime" />
      15       </body>
      16     </html>'''
      18     template2 = u'''
      19     <html>
      20       <body>
      21         <h1 tal:content="modules/time/asctime">time</h1>
      22       </body>
      23     </html>'''
      25     def createPage(self):
      26         root = self.getRootFolder()
      27         root['zptpage'] = ZPTPage()
      28         root['zptpage'].setSource(self.template, 'text/html')
      29         get_transaction().commit()
      31     def test_add(self):
      32         response = self.publish(
      33             "/+/zope.app.content.zpt.ZPTPage=",
      34             basic='mgr:mgrpw', 
      35             form={'add_input_name' : u'newzptpage',
      36                   'field.expand.used' : u'',
      37                   'field.source' : self.template,
      38                   'field.evaluateInlineCode.used' : u'',
      39                   'field.evaluateInlineCode' : u'on',
      40                   'UPDATE_SUBMIT' : 'Add'})
      42         self.assertEqual(response.getStatus(), 302)
      43         self.assertEqual(response.getHeader('Location'),
      44                          'http://localhost/@@contents.html')
      46         zpt = self.getRootFolder()['newzptpage']
      47         self.assertEqual(zpt.getSource(), self.template)
      48         self.assertEqual(zpt.evaluateInlineCode, True)
      50     def test_editCode(self):
      51         self.createPage()
      52         response = self.publish(
      53             "/zptpage/@@edit.html",
      54             basic='mgr:mgrpw', 
      55             form={'add_input_name' : u'zptpage',
      56                   'field.expand.used' : u'',
      57                   'field.source' : self.template2,
      58                   'UPDATE_SUBMIT' : 'Change'})
      59         self.assertEqual(response.getStatus(), 200)
      60         self.assert_(response.getBody().find('asctime">time</h1>') != -1)
      61         self.checkForBrokenLinks(response.getBody(), response.getPath(),
      62                                  'mgr:mgrpw')
      64     def test_index(self):
      65         self.createPage()
      66         t = time.asctime()
      67         response = self.publish("/zptpage", basic='mgr:mgrpw')
      68         self.assertEqual(response.getStatus(), 200)
      69         self.assert_(response.getBody().find(self.template2) != -1)
      70         self.checkForBrokenLinks(response.getBody(), response.getPath(),
      71                                  'mgr:mgrpw')
      73 def test_suite():
      74     return unittest.TestSuite((
      75         unittest.makeSuite(TemplatedPageTests),
      76         ))
      78 if __name__=='__main__':
      79     unittest.main(defaultTest='test_suite')

    o Line 25-29: This is the perfect example of a helper method. It
      creates a "Templated Page" content object called 'zptpage' . To
      write the new object to the ZODB, you have to commit the transaction
      using 'get' .

    o Line 31-48: To understand this test completely, it is surely
      helpful to be familiar with the way Zope 3 adds new objects and how the
      widgets create an HTML form. The "+"-sign in the URL is the adding view
      for a folder. The path that follows is simply the factory id of the
      content type (line 33).

      The form dictionary is another piece of information that must be
      carefully constructed. First of all, the 'field.expand.used' and
      'field.evaluateInlineCode.used' are required, whether you want
      to activate 'expand' and 'evaluateInlineCode' or not. It is
      required by the corresponding widgets. The 'add' key maps to the
      name the content object will recieve and 'UPDATE' just tells the
      form generator that the form was actually submitted and action should
      be taken. To make it easier on myself, I just created a "Templated
      Page" on the browser parallel to writing the functional test to get all
      of the above information.

      On line 42, we check whether the request was successful. Code
      302 signalizes a redirect and on line 43 we check that we are
      redirected to the correct page.

      Now, it is time to check in the ZODB whether the object has
      really been created and that all data was set correctly. On line 46 we
      retrieve the object itself and consequently we check that the source is
      set correctly and the 'evaluateInlineCode' flag was turned as
      request in the form (line 39).

    o Line 50-62: Before we can test whether the data of a "Templated
      Page" can be edited correctly, we have to create one. Here the 'createPage()' 
      method comes in handy, which quickly creates a page that we can use.
      Having done previous test already, the contents of the 'form' 
      dictionary should be obvious.

      Since the edit page returns itself, the status of the response
      should be 200. We also inspect the body of the response to make sure
      that the temlpate was stored correctly.

      One extremly useful feautre of the 'BrowserTestCase' is the
      check for broken links in the returned page. I would suggest that you
      do this test whenever a HTML page is returned by the response.

    o Line 64-71: Here we simply test the default view of the
      "Templated Page". No complicated forms or environments are needed. We
      just need to make sure that the template is executed correctly.


    
  Running Functional Tests


    The testing code directly depends on the Zope 3 source tree, so make
    sure to have it in you Python path. In Un*x/Linux you can do this using::

      export PYTHONPATH=$PYTHONPATH:<ZOPE3>/src 
        where '<ZOPE3>' is the path to you Zope 3 installation.
    Furthermore, functional tests depend on finding a file called 'ftesting.zcml' 
    , which is used to bring up the Zope 3 application server. Therefore it
    is best to just go to the directory '<ZOPE3>' , since there exists
    such a file. You can now execute our new funtional tests using::

      python path/to/ftest/test_templatedpage.py
        
    You will notice that these tests will take a couple seconds (5-10 seconds)
    to run. This is okay, since the functional tests have to bring up the
    entire Zope 3 system, which by itself will take about 4 seconds.  

  Exercises

    - Exercise 1: Add another functional test that checks the
      "Preview" and "Inline Code" screen.  

--
forwarded from http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/FunctionalTests



More information about the Zope3-dev mailing list