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

Mail Delivery System Mailer-Daemon at python.org
Tue Feb 17 07:50:10 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 iris1.directnic.com [204.251.10.81]: 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 1At4fA-0006lv-BY; Tue, 17 Feb 2004 07:49:20 -0500
From: zope3-dev at zope.org (srichter)
Reply-To: zope3-dev at zope.org
To: ;
Subject: [BasicUnitTests] (new) first draft (generated from LaTeX file)
Message-ID: <20040217074920EST 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/BasicUnitTests/subscribeform>
List-Unsubscribe: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/BasicUnitTests/subscribeform>
List-Archive: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/BasicUnitTests>
List-Help: <http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture>
Date: Tue, 17 Feb 2004 07:49:20 -0500
X-Spam-Status: OK (default 0.000)

Writing Basic Unit Tests

  Status

    IsDraft

  Authors

    StephanRichter

  Difficulty

    Newcomer Level  

  Skills

    - All you need to know is some Python.  


  Problem/Task

    As you know by now, Zope 3 gains its incredible stability from
    testing any code in great detail. The currently most common method is to
    write unit tests. This chapter introduces unit tests - which are Zope 3
    independent - and introduces some of the subtleties.  


  Recipe

    
  Implementing the Sample Class


    Before we can write tests, we have to have something to test. Here,
    we will implement a simple class called 'Sample' with a public
    attribute 'title' and description that is accessed via 'getDescription()' 
    and mutated using 'setDescription()' . Further, the description must
    be either a regular or a unicode string.

    Since this code will not depend on Zope, open a file named 'test' 
    anywhere and add the following class::

      01 Sample(object):
      02     """A trivial Sample object."""
      04     title = None
      06     def __init__(self):
      07         """Initialize object."""
      08         self._description = ''
      10     def setDescription(self, value):
      11         """Change the value of the description."""
      12         assert isinstance(value, (str, unicode)) 
      13         self._description = value
      15     def getDescription(self):
      16         """Change the value of the description."""
      17         return self._description

    o Line 4: The 'title' is just publicly declared and a value of 'None' 
      is given. Therefore this is just a regular attribute.

    o Line 8: The actual description string will be stored in '_' .

    o Line 12: Make sure that the description is only a regular or
      unicode string, like it was stated in the requirements.  


    If you wish you can now manually test the class with the interactive
    Python shell. Just start Python by entering 'python' in you shell
    prompt. Note that you should be in the directory in which 'test' is
    located when starting Python (an alternative is of course to specify the
    directory in your 'PYTHONPATH' .)::

      01 >>> from test_sample import Sample
      02 >>> sample = Sample()
      03 >>> print sample.title
      04 None
      05 >>> sample.title = 'Title'
      06 >>> print sample.title
      07 Title
      08 >>> print sample.getDescription()
      10 >>> sample.setDescription('Hello World')
      11 >>> print sample.getDescription()
      12 Hello World
      13 >>> sample.setDescription(None)
      14 Traceback (most recent call last):
      15   File "<stdin>", line 1, in ?
      16   File "test_sample.py", line 31, in setDescription
      17     assert isinstance(value, (str, unicode)) 
      18 AssertionError

    As you can see in the last test, non-string object types are not
    allowed as descriptions and an 'AssertionError' is raised.

    
  Writing the Unit Tests


    The goal of writing the unit tests is to convert the informal,
    manual, and interactive tests into a formal test class. Python provides
    already a module called 'unittest' which is a port of the Java-based
    unit testing product, JUnit, by Kent Beck and Erich Gamma. There are
    three levels to the testing framework (these derivate a bit from the
    original definitions as found in the Python library documentation. 'http://www.python.org/doc/current/lib/module-unittest.html' 
).

    The smallest unit is obviously the "test", which is a single method
    in a 'TestCase' class that tests the behavior of a small piece of
    code or a particular aspect of an implementation. The "test case" is then
    a collection tests that share the same setup/inputs. On top of all of
    this sits the "test suite" which is a collection of test cases and/or
    other test suites. Test suites combine tests that should be executed
    together. With the correct setup (as shown in the example below), you can
    then execute test suites. For large projects like Zope 3, it is useful to
    know that there is also the concept of a test runner, which manages the
    test run of all or a set of tests. The runner provides useful feedback to
    the application, so that various user interaces can be developed for it.

    But enough about the theory. In th following example, which you can
    simply put into the same file as your code above, you will see a test in
    common Zope 3 style.::

      01 import unittest
      03 class TestSample(unittest.TestCase):
      04     """Test the Sample class"""
      06     def test_title(self):
      07         sample = Sample()
      08         self.assertEqual(sample.title, None)
      09         sample.title = 'Sample Title'
      10         self.assertEqual(sample.title, 'Sample Title')
      12     def test_getDescription(self):
      13         sample = Sample()
      14         self.assertEqual(sample.getDescription(), '')
      15         sample._description = "Description"
      16         self.assertEqual(sample.getDescription(), 'Description')
      18     def test_setDescription(self):
      19         sample = Sample()
      20         self.assertEqual(sample._description, '')
      21         sample.setDescription('Description')
      22         self.assertEqual(sample._description, 'Description')
      23         sample.setDescription(u'Description2')
      24         self.assertEqual(sample._description, u'Description2')
      25         self.assertRaises(AssertionError, sample.setDescription, None)
      28 def test_suite():
      29     return unittest.TestSuite((
      30         unittest.makeSuite(TestSample),
      31         ))
      33 if __name__ == '__main__':
      34     unittest.main()

    o Line 3-4: We usually develop test classes, which must inherit
      from 'TestCase' . While often not done, it is a good idea to
      give the class a menaingful doc string that describes the purpose of
      the tests it includes.

    o Line 6, 12 & 18: When a test case is run, a method called 'runTests()' 
      is executed. While it is possible to overwrite this method to run tests
      differently, the default option is very sensible and is used everywhere
      in Zope 3. The method will look for any method whose name starts with 'test' 
      and execute it as a single test. This way we can create a "test method"
      for each aspect, method, function or property of the code to be tested.

      Note that there is no doc string for test methods. This is
      intentional. If a doc string is specified, it is used instead of the
      method name to identify the test. When specifying doc string, we have
      noticed that it is very difficult to identify the test later; therefore
      the method name is a much better choice.

    o Line 8, 10, 14,  
: The 'TestCase' class implements a handful of methods that do the
      testing for you. Here are some of the most frequently used ones. For a
      complete list see the standard Python documentation referenced above.

      -'assertEqual(first, second[, msg])' 
Checks whether the 'first' and 'second' value are equal. If the test
        fails, the 'msg' or 'None' is returned.

      -'assertNotEqual(first, second[, msg])' 
This is simply the opposite to 'assertEqual()' by checking for
        non-equality.

      -'assertRaises(exception, callable, ...)' 
You expect the 'callable' to raise 'exception' when executed. After
        the 'callable' you can specify any amount of positional and
        keyword arguments for the 'callable' . If you expect a group
        of exceptions from the execution, you can make 'exception' a
        tuple of possible exceptions.

      -'assert' 
Assert checks whether the specified expression executes correctly.
        If not, the test fails and 'msg' or 'None' is returned.

      -'failUnlessEqual()' 
This testing method is equivalent to 'assertEqual()' .

      -'failUnless(expr[, msg])' 
Equivalent to 'assert' .

      -'failif()' 
This is the opposite to 'failUnless()' .

      -'fail([msg])' 
Fails the running test without any evaluation. This is commonly use
        when testing various possible execution paths at once and you would
        like to signify a failure if an improper path was taken.  

    o Line 6-10: This method tests the 'title' attribute of the 'Sample' 
      class. The first test should be of course that the attribute exists and
      has the expected initial value (line 8). Then the title attribute is
      changed and we check whether the value was really stored. This might
      seem like overkill, but later you might change the title in a way that
      it uses properties instead. Then it becomes very important to check
      whether this test still passes.

    o Line 12-16: First we simply check that 'getDescription()' 
      returns the correct default value. Since we do not want to use other
      API calls like 'setDescription()' we set a new value of the
      description via the implementation-internal '_' attribute (line
      15). This is okay! Tests can make use of implementation-specific
      attributes and methods. Finally we just check that the correct value is
      returned.

    o Line 18-25: On line 21-24 it is checked that both regular and
      unicode strings are set correctly. In the last line of the test we make
      sure that no other type of objects can be set as a description and that
      an error is raised.

    o 28-31: This method returns a test suite that includes all test
      cases created in this module. It is used by the Zope 3 test runner when
      it picks up all available tests. You would basically add the line 'unittest.makeSuite(TestCaseClass)' 
      for each additional test case.

    o 33-34: In order to make the test module runnable by itself, you
      can execute 'unittest.main()' when the module is run.  


    
  Running the Tests


    You can run the test by simply calling 'python unittests.py' from
    the directory you saved the file in. When using the default Zope 3 test
    runner, tests will be picked up as long as they follow some conventions.
     

    - The tests must either be in a package or module called 'tests' .

    - If 'tests' is a package, then all test modules inside must also
      have a name starting with 'test' , as it is the case with our
      name 'test' .

    - The test module must be somewhere in the Zope 3 source tree,
      since the test runner looks only for files there.  


    You you can use the test runner to run only the sample tests as
    follows::

      python -vp test.py module.path.to.tests.test_sample
        The '-v' option stands for verbose mode, so that detailed
    information about a test failure is provided. The '-p' option
    enables a progress bar that tells you how many tests out of all have been
    completed. There are many more options that can be specified. You can get
    a full list of them with the option '-h' : 'python test.py -h' .


  Exercises

    - Exercise 1: It is not very common to do the setup - in our case
      'sample = Sample()' - in every test method. Instead there exists
      a method called 'setUp()' and its counterpart 'tearDown' that
      are run before and after each test, respectively. Change the test code
      above, so that it uses the 'setUp()' method. In later chapters
      and the rest of the book we will frequently use this method of setting
      up tests.

    - Exercise 2: Currently the 'test' test only verifies that None is
      not allowed as input value.

      - Improve the test, so that all other builtin types are tested
        as well.

      - Also, make sure that any objects inheriting from 'str' or 'unicode' 
        pass as valid values.  

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



More information about the Zope3-dev mailing list