As Odoo applications become more complex, controllers and APIs become a necessity for managing the interaction between the Odoo server and third-party services, web applications, mobile applications, and other elements of the front-end part. Although creating a controller is not complicated, ensuring its stability by means of proper testing is crucial.
Controller and API testing is necessary for the verification of the endpoints functionality, checking of security and permissions, response formats, etc., as well as for prevention of regressions after any further updates. Odoo 19 offers an effective testing framework to enable the automation of these checks and high-quality application development.
This blog will provide an example of testing HTTP and JSON API controllers in Odoo 19 with the help of a testing framework.
Understanding Controllers in Odoo 19
It is the duty of controllers to process HTTP requests and send appropriate responses.
An example of a simple controller:
from odoo import http
class StudentController(http.Controller):
@http.route('/api/student', type='json', auth='user')
def get_student(self):
return {
'status': 'success',
'message': 'Student API working'
}
In this example,
- /api/student is the endpoint.
- type='json' defines the response will be JSON.
- auth='user' gives access only to the user.
Whereas the endpoint might work during development, automated tests will always help ensure its continued functionality after code updates.
Why Test Controllers and APIs?
The main advantages of controller testing are as follows:
- Verifies functionality of endpoints.
- Ensure proper authentication and access rights.
- Ensures consistency of response structures.
- Identifies issues before implementation.
- Protects from regressions in upgrades.
- Increases the reliability of applications.
In the absence of automation, the slightest change in the controller code can ruin all integrations and front-end functionalities.
Odoo Testing Framework
There are different test classes offered by Odoo:
TransactionCase
It is used for testing ORM operations and business logic.
from odoo.tests.common import TransactionCase
HttpCase
It is used for testing controllers, routes, and web interaction.
from odoo.tests import HttpCase
HttpCase class is the most apt option for testing controllers and APIs as it enables simulating HTTP requests.
Test Module Structure
Structure of controller tests:
my_module/
+-- controllers/
¦ +-- main.py
+-- tests/
¦ +-- __init__.py
¦ +-- test_controller.py
+-- __init__.py
+-- __manifest__.py
Creating a Controller Test
Take the following controller as an example:
from odoo import http
class StudentController(http.Controller):
@http.route('/student/info', auth='public', type='http')
def student_info(self):
return "Student Information"
Create a test case:
from odoo.tests import HttpCase, tagged
@tagged('-at_install', 'post_install')
class TestStudentController(HttpCase):
def test_student_info_route(self):
response = self.url_open('/student/info')
self.assertEqual(
response.status_code,
200
)
The above test will check if the endpoint is working and give the responses successfully.
Testing JSON API Controllers
JSON endpoints are mainly used for integrations and front-end communications.
Controller:
from odoo import http
class StudentAPI(http.Controller):
@http.route('/api/student', type='json', auth='public')
def get_student(self):
return {
'name': 'John',
'age': 22
}
Test:
import json
from odoo.tests import HttpCase, tagged
@tagged('-at_install', 'post_install')
class TestStudentAPI(HttpCase):
def test_student_api(self):
response = self.url_open(
'/api/student',
data=json.dumps({}),
headers={
'Content-Type': 'application/json'
}
)
data = response.json()
self.assertEqual(
data['name'],
'John'
)
This makes sure that the API sends back the expected data structure.
Testing Authentication
Most APIs require authenticated access.
Controller:
@http.route(
'/secure/data',
auth='user',
type='json'
)
def secure_data(self):
return {'status': 'authorized'}
Test authenticated access:
self.authenticate(
'admin',
'admin'
)
response = self.url_open('/secure/data')
self.assertEqual(
response.status_code,
200
)
Authentication testing is essential since wrong access could lead to exposing sensitive company information.
Testing Authorization and Permissions
Apart from authentication, proper permissions are required for APIs.
Example:
user = self.env['res.users'].create({
'name': 'Demo User',
'login': 'demo_user',
'password': 'demo123'
})Authenticate as the new user and check that restricted resources are still unreachable.
This makes sure that proper security checks are applied.
Testing POST Requests
APIs often create records using the POST method.
Controller:
@http.route(
'/api/student/create',
type='json',
auth='user'
)
def create_student(self, name):
student = request.env[
'student.student'
].create({
'name': name
})
return {
'id': student.id
}
Test:
response = self.opener.post(
'/api/student/create',
json={
'name': 'Michael'
}
)
self.assertEqual(
response.status_code,
200
)
The above test will check if creating records works properly.
Testing Response Content
Just checking the status code is not enough.
Always check:
- Response keys.
- Values returned.
- Types of data returned.
- Structure of the response.
Example:
data = response.json()
self.assertIn(
'id',
data
)
self.assertIsInstance(
data['id'],
int
)
This will help in making sure that the client receives the expected response.
Testing Error Scenarios
Reliable APIs must work well during failures.
Example controller:
@http.route(
'/api/student/details',
type='json',
auth='user'
)
def student_details(
self,
student_id
):
student = request.env[
'student.student'
].browse(student_id)
if not student.exists():
return {
'error': 'Student not found'
}
return {
'name': student.name
}
Test:
response = self.opener.post(
'/api/student/details',
json={
'student_id': 9999
}
)
data = response.json()
self.assertEqual(
data['error'],
'Student not found'
)
Testing failure scenarios is equally essential as testing positive scenarios.
Running Controller Tests
Enable tests during installation or upgrade of a module:
odoo-bin -d test_db \
-u my_module \
--test-enable
Run tests using tags:
odoo-bin -d test_db \
--test-tags my_module
Look at the logs carefully to find the failed assertions and debugging info.
Best Practices for Controller/API Testing
- Test Both Success and Failure Cases
Testing must include failure,success scenarios instead of testing only successful ones.
- Invalid data
- Missing parameters
- Unauthorized access
- Permission violation
- Avoid Hardcoded IDs
Create test records dynamically rather than relying on database records.
- Keep Tests Independent
Each test must be independent of other tests.
- Verify Response Structure
Check values and response keys too, instead of just checking the status code.
- Use Meaningful Assertions
A clear assertion makes debugging easy in case of failures in testing.
- Test Security Rules
Make sure that authentication, authorization, and access permissions are properly checked.
Common Mistakes
Developers sometimes face the following issues:
- Forgetting test tags.
- Using live data for testing.
- Missing error scenarios.
- Status code testing only.
- Not validating authentication.
- Writing dependent test cases.
- Hard coding record IDs.
By avoiding the above errors, a better test suite is achieved.
Testing controllers and APIs is one of the crucial stages in developing applications for Odoo 19. Thanks to the HttpCase testing framework, the developer will be able to automate the testing of endpoints, authenticate and authorize, validate the structure of responses, etc.
A well-tested API makes it possible to increase the stability of the application, reduce maintenance costs, and feel confident about implementing new features. As a result, a lot of efforts spent on controller testing will pay off.
To read more about The Ultimate Guide to API Testing: 9 Testing Methods You Can’t Ignore, refer to our blog The Ultimate Guide to API Testing: 9 Testing Methods You Can’t Ignore.