[IMP] doc: add testing reference and improve docstrings
authorXavier Morel <xmo@openerp.com>
Thu, 9 Oct 2014 13:38:36 +0000 (15:38 +0200)
committerXavier Morel <xmo@openerp.com>
Thu, 9 Oct 2014 13:38:36 +0000 (15:38 +0200)
doc/reference.rst
doc/reference/cmdline.rst
doc/reference/testing.rst [new file with mode: 0644]
openerp/addons/base/tests/test_xmlrpc.py
openerp/tests/common.py

index bf0ad5d..a3dd978 100644 (file)
@@ -12,6 +12,7 @@ Reference
     reference/module
     reference/cmdline
     reference/security
+    reference/testing
 
     reference/http
     reference/qweb
index 140405d..e3f2952 100644 (file)
@@ -38,6 +38,10 @@ Running the server
     (:file:`{$HOME}/.openerp_serverrc` by default, overridable using
     :option:`-c`)
 
+.. option:: --test-enable
+
+    runs tests after installing modules
+
 .. _reference/cmdline/scaffold:
 
 Scaffolding
diff --git a/doc/reference/testing.rst b/doc/reference/testing.rst
new file mode 100644 (file)
index 0000000..2676c2b
--- /dev/null
@@ -0,0 +1,73 @@
+.. _reference/testing:
+
+===============
+Testing Modules
+===============
+
+Odoo provides support for testing modules using unittest2_.
+
+To write tests, simply define a ``tests`` sub-package in your module, it will
+be automatically inspected for test modules. Test modules should have a name
+starting with ``test_`` and should be imported from ``tests/__init__.py``,
+e.g.
+
+.. code-block:: text
+
+    your_module
+    |-- ...
+    `-- tests
+        |-- __init__.py
+        |-- test_bar.py
+        `-- test_foo.py
+
+and ``__init__.py`` contains::
+
+    from . import test_foo, test_bar
+
+.. note:: test modules which are not imported from ``tests/__init__.py`` will
+          not be run
+
+The test runner will simply run any test case, as described in the official
+`unittest documentation`_, but Odoo provides a number of utilities and helpers
+related to testing Odoo content (modules, mainly):
+
+.. autoclass:: openerp.tests.common.TransactionCase
+    :members: browse_ref, ref
+
+.. autoclass:: openerp.tests.common.SingleTransactionCase
+    :members: browse_ref, ref
+
+By default, tests are run once right after the corresponding module has been
+installed. Test cases can also be configured to run after all modules have
+been installed, and not run right after the module installation:
+
+.. autofunction:: openerp.tests.common.at_install
+
+.. autofunction:: openerp.tests.common.post_install
+
+The most common situation is to use
+:class:`~openerp.tests.common.TransactionCase` and test a property of a of a
+model in each method::
+
+    class TestModelA(common.TransactionCase):
+        def test_some_action(self):
+            record = self.env['model.a'].create({'field': 'value'})
+            record.some_action()
+            self.assertEqual(
+                record.field,
+                expected_field_value)
+
+        # other tests...
+
+Running tests
+-------------
+
+Tests are automatically run when installing or updating modules if
+:option:`--test-enable <odoo.py --test-enable>` was enabled when starting the
+Odoo server.
+
+As of Odoo 8, running tests outside of the install/update cycle is not
+
+
+.. _unittest2: http://pypi.python.org/pypi/unittest2
+.. _unittest documentation: https://docs.python.org/2/library/unittest.html
index 1d89986..d8e6528 100644 (file)
@@ -1,18 +1,16 @@
 # -*- coding: utf-8 -*-
-import time
-import unittest2
-import xmlrpclib
-
 import openerp.tests.common
 
 DB = openerp.tests.common.DB
 
 class test_xmlrpc(openerp.tests.common.HttpCase):
+    at_install = False
+    post_install = True
 
     def test_01_xmlrpc_login(self):
         """ Try to login on the common service. """
         uid = self.xmlrpc_common.login(DB, 'admin', 'admin')
-        self.assertTrue(uid == 1)
+        self.assertEqual(uid, 1)
 
     def test_xmlrpc_ir_model_search(self):
         """ Try a search on the object service. """
@@ -22,12 +20,4 @@ class test_xmlrpc(openerp.tests.common.HttpCase):
         ids = o.execute(DB, 1, 'admin', 'ir.model', 'search', [], {})
         self.assertIsInstance(ids, list)
 
-    # This test was written to test the creation of a new RPC endpoint, not
-    # really for the EDI itself.
-    #def test_xmlrpc_import_edi_document(self):
-    #    """ Try to call an EDI method. """
-    #    msg_re = 'EDI Document is empty!'
-    #    with self.assertRaisesRegexp(Exception, msg_re):
-    #        self.proxy.edi_60.import_edi_document(DB, ADMIN_USER_ID, ADMIN_PASSWORD, {})
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index d2db62c..b3f6dbe 100644 (file)
@@ -17,8 +17,6 @@ import unittest2
 import urllib2
 import xmlrpclib
 from datetime import datetime, timedelta
-from shutil import rmtree
-from tempfile import mkdtemp
 
 import werkzeug
 
@@ -47,7 +45,8 @@ def at_install(flag):
     whether the test should (``True``) or should not (``False``) run during
     module installation.
 
-    By default, tests are run at install.
+    By default, tests are run right after installing the module, before
+    starting the installation of the next module.
     """
     def decorator(obj):
         obj.at_install = flag
@@ -59,7 +58,8 @@ def post_install(flag):
     specifying whether the test should or should not run after a set of
     module installations.
 
-    By default, tests are *not* run after installation.
+    By default, tests are *not* run after installation of all modules in the
+    current installation set.
     """
     def decorator(obj):
         obj.post_install = flag
@@ -78,10 +78,13 @@ class BaseCase(unittest2.TestCase):
         return self.registry.cursor()
 
     def ref(self, xid):
-        """ Returns database ID corresponding to a given identifier.
+        """ Returns database ID for the provided :term:`external identifier`,
+        shortcut for ``get_object_reference``
 
-            :param xid: fully-qualified record identifier, in the form ``module.identifier``
-            :raise: ValueError if not found
+        :param xid: fully-qualified :term:`external identifier`, in the form
+                    :samp:`{module}.{identifier}`
+        :raise: ValueError if not found
+        :returns: registered id
         """
         assert "." in xid, "this method requires a fully qualified parameter, in the following form: 'module.identifier'"
         module, xid = xid.split('.')
@@ -89,10 +92,13 @@ class BaseCase(unittest2.TestCase):
         return id
 
     def browse_ref(self, xid):
-        """ Returns a browsable record for the given identifier.
+        """ Returns a record object for the provided
+        :term:`external identifier`
 
-            :param xid: fully-qualified record identifier, in the form ``module.identifier``
-            :raise: ValueError if not found
+        :param xid: fully-qualified :term:`external identifier`, in the form
+                    :samp:`{module}.{identifier}`
+        :raise: ValueError if not found
+        :returns: :class:`~openerp.models.BaseModel`
         """
         assert "." in xid, "this method requires a fully qualified parameter, in the following form: 'module.identifier'"
         module, xid = xid.split('.')
@@ -100,15 +106,17 @@ class BaseCase(unittest2.TestCase):
 
 
 class TransactionCase(BaseCase):
-    """
-    Subclass of BaseCase with a single transaction, rolled-back at the end of
-    each test (method).
+    """ TestCase in which each test method is run in its own transaction,
+    and with its own cursor. The transaction is rolled back and the cursor
+    is closed after each test.
     """
 
     def setUp(self):
         self.registry = RegistryManager.get(DB)
+        #: current transaction's cursor
         self.cr = self.cursor()
         self.uid = openerp.SUPERUSER_ID
+        #: :class:`~openerp.api.Environment` for the current test case
         self.env = api.Environment(self.cr, self.uid, {})
 
     def tearDown(self):
@@ -117,9 +125,9 @@ class TransactionCase(BaseCase):
 
 
 class SingleTransactionCase(BaseCase):
-    """
-    Subclass of BaseCase with a single transaction for the whole class,
-    rolled-back after all the tests.
+    """ TestCase in which all test methods are run in the same transaction,
+    the transaction is started with the first test method and rolled back at
+    the end of the last.
     """
 
     @classmethod
@@ -155,7 +163,7 @@ class RedirectHandler(urllib2.HTTPRedirectHandler):
     https_response = http_response
 
 class HttpCase(TransactionCase):
-    """ Transactionnal HTTP TestCase with url_open and phantomjs helpers.
+    """ Transactional HTTP TestCase with url_open and phantomjs helpers.
     """
 
     def __init__(self, methodName='runTest'):