diff --git a/doc/reference.rst b/doc/reference.rst index bf0ad5d6d1a..a3dd9783ff8 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -12,6 +12,7 @@ Reference reference/module reference/cmdline reference/security + reference/testing reference/http reference/qweb diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst index 140405d0037..e3f2952c5d3 100644 --- a/doc/reference/cmdline.rst +++ b/doc/reference/cmdline.rst @@ -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 index 00000000000..2676c2b4084 --- /dev/null +++ b/doc/reference/testing.rst @@ -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 ` 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 diff --git a/openerp/addons/base/tests/test_xmlrpc.py b/openerp/addons/base/tests/test_xmlrpc.py index 1d899866ab1..d8e6528bc18 100644 --- a/openerp/addons/base/tests/test_xmlrpc.py +++ b/openerp/addons/base/tests/test_xmlrpc.py @@ -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: diff --git a/openerp/tests/common.py b/openerp/tests/common.py index d2db62c21a0..b3f6dbe81a3 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -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'):