--- /dev/null
+.. _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
# -*- 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. """
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:
import urllib2
import xmlrpclib
from datetime import datetime, timedelta
-from shutil import rmtree
-from tempfile import mkdtemp
import werkzeug
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
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
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('.')
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('.')
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):
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
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'):