In this tutorial I will learn you how to write tests, how to use the tests in Odoo and how to test them. I will create an example test that creates a new project, a new task and attaches the project to the new task.
Tip: This tutorial is based on Odoo V11 but will also work in V10.
1. Creating a new module
Let us create a new module to start. You can either manually create a module or create one with the scaffold command from Odoo. Now that you’ve created your module you should add the dependency to the module where you want to create a test for. In this tutorial I will create a test for the project module so I’ll add a dependency for ‘project’. Add your depends in the manifest.py (or __openerp__.py in V10):
1 |
'depends': ['project'], |
2. Creating the Python files
After you’ve created your new module you should create a new folder named ‘tests’. Create a new Python file for the test, in this example I’ll create a new file named ‘test_project.py’.
Tip: The filename of a Python test should always start with ‘test_’ or it will not be executed! Your folder structure should now look like this:
Next create a new Python file named __init__.py and import the Python file ‘test_project’ in it:
1 2 3 |
# -*- coding: utf-8 -*- from . import test_project |
Your folder structure should now look like this:
Tip: You should not import the tests folder in the main __init__.py file. Odoo tests are an exception and have a built-in check by Odoo. They do not need an explicit import like models or controllers.
3. Creating the class and adding imports
Now open up your ‘test_project.py’ file again and let us start by adding the import and creating the class structure!
In order to write a test you should import ‘odoo.tests’ and you should create a new class with a TransactionCase. Have a look at this code:
1 2 3 4 5 6 7 |
# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.tests import common class TestProject(common.TransactionCase): |
So, does this make sense to you? We simply import the default Odoo framework options for tests with ‘from odoo.tests import common’. We then create a new class in which we add ‘common.TransactionCase’. The ‘common.TransactionCase’ tells Odoo that we want to test a transaction and that we want to do a rollback of this record after the test is done.
4. Writing the tests
Now that we have the basic structure setup there is just one more thing to do: write a function in which you want to do your tests. Go ahead and create a new function in your file.
Tip: Function names for tests should always start with ‘test_’ or they will not be run by Odoo!
Finally, write the test cases you’d like in this function. In my example I will create a test that creates a new project, then creates a new task and then adds the project to the task. Have a look at this piece of code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def test_create_data(self): # Create a new project with the test test_project = self.env['project.project'].create({ 'name': 'TestProject' }) # Add a test task to the project test_project_task = self.env['project.task'].create({ 'name': 'ExampleTask', 'project_id': test_project.id }) # Check if the project name and the task name match self.assertEqual(test_project.name, 'TestProject') self.assertEqual(test_project_task.name, 'ExampleTask') # Check if the project assigned to the task is in fact the correct id self.assertEqual(test_project_task.project_id.id, test_project.id) # Do a little print to show it visually for this demo - in production you don't really need this. print('Your test was succesfull!') |
So, does this make sense to you? A lot of the code looks the same as if you’re writing custom code. With self.env[‘project.project’].create({}) we’ll create a new project named ‘TestProject’. With self.env[‘project.task’].create({}) we’ll create a new task named ‘ExampleTask’ and we will assign the project to this task, thanks to the ‘test_project’ variable.
If you would run this code it would create a new project and task and the moment the test is finished the transaction is rolled back (so there is no record in the database).
4.1 Understanding assertEqual
The final part of my code shows some assertEqual calls, so what do these calls do? assertEqual is made available by default in Odoo to test and compare values. For example:
1 |
self.assertEqual(test_project.name, 'TestProject') |
This will test if the just created project record it’s name is indeed named ‘TestProject’. In the second assertEqual I check if the task name is in fact ‘ExampleTask’. And, finally, in the third assertEqual I check if the project attached to the task is indeed the right project:
1 |
self.assertEqual(test_project_task.project_id.id, test_project.id) |
The first variable of assertEqual is always the value you’ve gotten back from the test and the second part is the result that you’re expecting. If there is a difference in those two the test will fail.
5. Running / testing the test
So now we’ve coded the whole test but how do we run and test it? In order to run a test you should use the ‘–test-enable’ parameter and the ‘-i’ parameter to say which module(s) you want to test. If you want to test this in your local Odoo instance you will need to run your Odoo from the terminal like this:
1 2 |
./odoo-bin -i automated_test_demo --test-enable -c /etc/odoo11-server.conf |
If you have a runbot instance (or use Odoo.sh) the tests will be executed automatically and you will see if the test succeeded or failed right away. An example of Odoo.sh:
An example of a runbot instance:
When you just run the tests locally you can see if the test succeeded or failed in the logfile. Open up your logfile after you’ve ran the test and have a look at the output:
1 2 |
INFO Newest odoo.addons.automated_test_demo.tests.test_project: Ran 1 test in 0.566s INFO Newest odoo.addons.automated_test_demo.tests.test_project: OK |
If you add a print statement in the test, like in my example, you will also see it printed in the terminal:
5.1 Making a test fail
We’ve now seen how to create a test and how to run it, but how exactly does a failed test look?
Open up your test again and change the assertEqual from this:
1 |
self.assertEqual(test_project.name, 'TestProject') |
to this:
1 |
self.assertEqual(test_project.name, 'TestProject11') |
Run the test from your terminal again with -i your_module –test-enable in the command. You’ll see that the print statement is no longer printed in your terminal. Now open up your logfile again after your Odoo is loaded. You’ll now find a failure in the logfile:
1 2 3 4 5 |
ERROR Newest odoo.addons.automated_test_demo.tests.test_project: ` AssertionError: 'TestProject' != 'TestProject11' INFO Newest odoo.addons.automated_test_demo.tests.test_project: Ran 1 test in 1.741s ERROR Newest odoo.addons.automated_test_demo.tests.test_project: FAILED INFO Newest odoo.addons.automated_test_demo.tests.test_project: (failures=1) |
This shows you that the input ‘TestProject’ does not match what we expect (‘TestProject11’) and because of this our test failed.
If you would commit these changes to Github and if you’re using a runbot or Odoo.sh you’ll also see a failed instance after this.
6. Conclusion
Writing and using tests in Odoo is quite easy to do. It is a great feature to use if you’re writing a lot of custom code and want to keep overview. Thanks to Odoo.sh and runbots it is also very easy to follow up on your tests and to see if something has broken or not. Sadly at this point running tests locally isn’t very easy to follow up though so you might want to consider setting up a runbot or Odoo.sh account.
Do you want to try the demo code and see the source code of this tutorial? You can view it on my Github account.
Has this tutorial helped you, do you have any feedback or questions? Post away!
Hey Yen,
Thank you for the tutorial, it is pretty clear and I will most likely use it as resources if I need to teach on the subject.
Little details, to make it closer to perfection,
assertEqual
, as good practice, should take the expected value as first argument. The second argument should be the value coming from the test.Additionally,
assertEqual
has ahelp=
key argument, that would worth to expose in this document I would say. What do you think?Hi Jordi,
Thanks a lot for the feedback! Do you have any link to prove the fact that the second argument should be the value coming from the test? For example, if you look through all Odoo you will see all assertEqual statements are reversed to what you say.
First, thank you for that tuto. It will be usefull to increase the quality of codes.
Then, correct me if I’m wrong, you should precise that running test (–test-enable) will run all depencies test : if your module depends on Sales module, it will run all tests from Sales module (and his dependency). Right ?
Quite helpful and descriptive. Thank You.
You’re welcome, happy that you like it!
Flanker is not compatible with Python 3, email validation has been disabled????????
pip install flanker
install it but that’s still coming out or there’s another configuration
Great article Yen, Thanks for sharing.
In my case I’ll be doing automated web tests in Odoo using Selenium WebDriver. I just wanted to know if the process of creating these tests would look like the one described in the article, or will I need to document myself more in order to run test cases with Selenium.
Thanks in advance
Hi Skander,
I’m sorry but I’ve never used Selenium before so I’m afraid I can’t answer this one for you. Perhaps try to ask this on the official help forums?
Can we check code coverage with automated tests in Odoo ? Thanks
Hi Thuan,
Yes that is possible, just like with any other Python program. You can find some default tutorials about this subject on the internet and apply that.