Odoo has a built-in testing framework. Python unit tests for business logic, HTTP tests for controllers, and tours (browser tests) for frontend. Combined with CI pipelines, you catch errors before they reach production.
Unit tests
Inherit from odoo.tests.common.TransactionCase (each test runs in a transaction that's rolled back) or SavepointCase (setUpClass runs once, tests share data). Create test data in setUp, call methods, verify with self.assertEqual and self.assertRaises.
HTTP tests
Inherit from HttpCase. Test controllers, web endpoints, and JSON-RPC calls. Use self.url_open() for GET and POST. Verify status codes and response body.
Tours
Browser-based tests simulating user interaction. Define steps: click menu, fill field, click button, verify result. Tours run in headless Chrome (or Chromium). Good for testing complete flows: create quote, confirm, invoice.
Running tests
odoo --test-enable --stop-after-init -d test_db --init my_module. Tests run during module installation. --test-tags filters specific tests. In Docker: docker exec odoo odoo --test-enable....
CI with GitHub Actions
Set up a GitHub Action that starts PostgreSQL and Odoo, installs your module, and runs tests. OCA has a reference workflow (oca/oca-ci) that handles most of it. Pull requests that break tests are automatically blocked.
What to test
Computed fields: provide input, verify output. Constraints: try creating invalid data, verify exception is raised. Workflows: create order, confirm, invoice, check state after each step. Not everything needs tests, but business-critical logic should always have them.