How To - euvm-odoo/internal-documentation GitHub Wiki
The odoo unit test models and libraries ar built based on the python unittest library.
To do this we have to add the flag --test-enable to either odoo-bin or odev tu run the tests before executing the database. The way to run them are the following:
Example with odoo-bin:
./odoo-bin -d <database-name> --addons-path:<modules-path> --test-enable
Example with odev:
odev run <database-name> <modules-path> -i <modules> --test-enable
In order to make odoo-bin find the tests we have to put them inside the following directory structure:
Module |-- tests |-- test_files |-- init.py |-- test_file.py
In the init.py file we import all of the tests inside of our test_files folder. It's important to mention that we don't have to import the tests module inside our main init.py folder.
Whenever we create a unit test in odoo we can inherit from the base test class or we can inherit from a class of a test from a specific module. If we want to create a clase with the base test class we have to import and inherit TransactionCase, the code would be the following:
from odoo.tests.common import TransactionCase
class TestForNewModule(TransactionCase)
If we inherit from another test class we can use the data that is already defined inside of the class. This is useful in case our module modifies some functionallity of the sale module or another module, and instead of us having to create some records for testing we can just use those that have been created for the test class, so we can access to some records without the need to explicitly writing how to create them with the ORM.
from odoo.addons.sale.tests.common import SaleCommon
class TestCreatePaymentFromSaleOrder(SaleCommon):
When creating a class for a python unittest we can use a method called setUpClass. The setupClass is method is used to instantiate the data that we want to use in all of our testing methods. This method is the first one that is called when running the tests and after the tests have finished the data instantiated is erased. Here we create the different objects that we'll later use by using the create method in the ORM. In case we have inherited another test we can call the super setUpClass to have in memory the records from the parent class.
There are two types of setUpClass inside of the python unittest. The first one, the one that's mostly used in odoo, has the @classmethod wrapper. This wrapper is used to make the method work based on the class than based on each of the instances of the class. When we add this wrapper the setUpClass is executed once and the data is saved for each of the tests.
The second one doesn't use the @classmethod wrapper. In this case the setUpClass will be called before each of the tests mehotds are executed. We do this to recreate the data in case it was modified in a previous test, but in Odoo this is hardly ever used.
Here is an example on how to create products using the first type of setUpClass:
@classmethod
def setUpClass(cls):
super().setUpClass()
# Update product
cls.product.write({
'min_width': 10.0, 'min_height': 10.0, 'lst_price': 10.0
})
# Create new product
cls.product_2 = cls.env['product.product'].create({
'name': 'Test Product 2',
'detailed_type': 'consu',
'list_price': 20.0,
'min_width': 5.0,
'min_height': 5.0,
'categ_id': cls.product_category.id
})
# Create new sale order
clas.sale_order = cls.env['sale.order'].create({
'partner_id': cls.partner.id
})
A test method should run only one test at the time. All of the test method most be named following the convention of test_*, otherwise they won't be identified as test methods and won't be executed. Inside the tests we can access to all of the record already instantiated inside the class and create new ones. Inside of the test there is always one or more assert method from the unittest library. This methods will test a condition, if the condition is not met the test will fail and an error will be generated indicating on which function this happened.
Here is an example of a test method
def test_idk_contact_updates(self):
self.assertEqual(self.idk_contact.name, "A Partner", "idk_contact.name is not set on the activity: {self.idk_contact}")
message_id = self.activity_id.action_feedback() # Post message and unlink activity
message = self.env["mail.message"].browse(message_id)
self.assertEqual(message.idk_contact.name, "A Partner", f"idk_contact.name not set on message: {message}")
self.assertEqual(self.idk_contact, message.idk_contact, "idk_contact specified on the activity: {self.idk_contact} is not the same record as the one on the message: {message.idk_contact}")