Introduction
Testing in Odoo is no longer a luxury—it’s a necessity. Whether you’re building a new module or customizing an existing one, writing robust unit and integration tests is your safety net against future failures. In this guide, we’ll walk through how to use PyTest, one of the most powerful testing frameworks, to build clean, scalable, and maintainable tests for your Odoo modules.
📐 Understanding the Testing Pyramid
Unit Tests vs Integration Tests vs End-to-End Tests
The testing pyramid is a simple but powerful concept:
- Unit Tests focus on isolated pieces of logic (e.g., methods or computed fields).
- Integration Tests test how different components (like models, workflows, and APIs) interact together.
- End-to-End Tests simulate real user actions in the frontend/backend UI.
Where Odoo Developers Often Get It Wrong
Many developers either over-test or under-test. Writing only end-to-end tests can be slow and flaky. On the other hand, ignoring integration logic results in false positives. The sweet spot? A solid mix of unit and integration tests with PyTest.
Explore the key differences between Odoo REST API and XML-RPC to determine the best integration method for scalability, performance, and future-proofing your business systems in 2025.
🔧 Getting Started with PyTest for Odoo
Installing and Configuring PyTest for Odoo
First things first. To use PyTest with Odoo:
bash
pip install pytest pytest-mock
Make sure to create a pytest.ini file at your project root:
ini
[pytest]
python_files = test_*.py
addopts = –tb=short -p no:warnings
Setting Up the Odoo Testing Environment
Use odoo-bin with the –test-enable flag and load your test module using -i. For PyTest integration, simply create your tests directory inside your custom module and start writing test_*.py files.
🧪 Writing Unit Tests in Odoo Using PyTest
What to Test in a Unit Test
- Pure business logic
- Computed fields
- Domain filtering
- Utility methods
How to Mock Odoo Models
Use pytest-mock to replace calls to actual models:
python
def test_discount_applied(mocker):
mock_discount = mocker.patch(‘odoo.addons.my_module.models.sale_order.SaleOrder._compute_discount’)
mock_discount.return_value = 10
Best Practices for Writing Isolated Unit Tests
- Never rely on the database unless necessary
- Use mocking to simulate behaviors
- Assert expected inputs and outputs clearly
🌐 Writing Integration Tests in Odoo Using PyTest
When Integration Tests Are Necessary
Use them when testing:
- Workflows (e.g., leads → opportunities → sales orders)
- Model relations (One2many, Many2one)
- Access rights and security rules
Simulating Real Use Cases
Integration tests often use real records:
python
def test_create_invoice(env):
sale_order = env[‘sale.order’].create({…})
invoice = sale_order._create_invoices()
assert invoice.state == ‘draft’
Managing Data Dependencies
Avoid flaky tests by using @classmethod to set up test data with setUpClass.
📁 Structuring Your Test Files and Folders
Recommended Directory Structure
text
my_module/
├── models/
├── tests/
│ ├── __init__.py
│ ├── test_models.py
│ ├── test_workflows.py
Naming Conventions for PyTest in Odoo
- Use test_*.py for test files
- Use test_ prefix for functions
- Group related tests into classes
🧰 Setting Up Fixtures for Odoo Tests
PyTest Fixtures vs Odoo’s Test Data
Fixtures in PyTest help you avoid duplication:
python
@pytest.fixture
def test_user(env):
return env[‘res.users’].create({…})
Creating Reusable Fixtures for Speed and Clarity
Reusability = maintainability. Abstract common setups like creating users, products, or partners.
🐞 Common Testing Challenges in Odoo
Dealing with External APIs in Tests
Mock API responses:
python
mocker.patch(‘requests.get’, return_value=Mock(status_code=200, json=lambda: {“data”: “ok”}))
Timezones, Currencies, and Multi-Company Testing
Always set the environment context explicitly:
python
env = env(context=dict(tz=’UTC’, lang=’en_US’, company_id=company.id))
🧪 Debugging and Improving Failing Tests
Using PyTest's Output to Track Errors
Enable verbose mode:
bash
pytest -v –capture=no
Logging and Assert Messages for Debugging
Use custom messages:
python
assert result == expected, f”Expected {expected}, but got {result}”
🤖 Automating Test Runs with CI/CD
Integrating Tests with GitHub Actions or GitLab CI
Use this GitHub Actions snippet:
yaml
– name: Run PyTest
run: |
pip install -r requirements.txt
pytest
Running Tests on Every Push for Safer Deployments
CI tools help you catch bugs early—don’t skip this step!
⚡ Performance Tips for Running Large Test Suites
Selective Test Execution with PyTest Markers
Use markers like:
bash
pytest -m “unit”
In code:
python
@pytest.mark.unit
def test_example(): …
Parallel Test Execution with pytest-xdist
Install with:
bash
pip install pytest-xdist
Run with:
bash
pytest -n auto
🧩 Writing Tests for Odoo Custom Modules
Testing Business Logic and Workflows
Custom logic needs custom coverage. Always verify your triggers, computed fields, and button actions.
How to Test Security Rules and Access Rights
Use sudo() and with_user() methods to simulate different user groups.
🔍 Case Study: Testing a Custom Sales Workflow
Real Example with Fixtures and Assertions
python
def test_sales_approval_flow(sale_order):
sale_order.action_confirm()
assert sale_order.state == ‘sale’
Lessons Learned
- Keep it modular
- Reuse fixtures
- Always clean up test data
✅ Best Practices for Long-Term Test Maintenance
Clean Code = Clean Tests
Don’t let your tests become spaghetti. Refactor aggressively.
Keeping Tests in Sync with Module Changes
When features change, update tests immediately. Outdated tests are worse than none.
📌 Conclusion
Writing robust unit and integration tests with PyTest for your Odoo modules doesn’t just protect your code—it safeguards your business logic and streamlines deployments. Whether you’re a developer, DevOps engineer, or project lead, building test coverage into your process will pay dividends in reliability and performance.
Need help writing or reviewing Odoo tests?
✅ Book a free technical consultation with our expert team today and build smarter with confidence.
Let’s build better Odoo apps—faster, safer, and stronger.
🛠️ Visit our LinkedIn page – Odoo Vanguard and stay connected with innovation in Odoo DevOps.
❓Frequently Asked Questions
1. Can I run PyTest tests without using the Odoo test runner?
Yes, PyTest can run standalone tests, but ensure the Odoo environment is initialized correctly using fixtures or setup.
2. How do I write tests for workflows using buttons in Odoo?
Simulate button clicks via record.button_name() and assert the changes to fields and state.
3. Is there a way to reuse test data across multiple test files?
Yes, by placing your fixtures in conftest.py, you can reuse them across the entire test suite.
4. Should I write tests for every method?
Not necessarily. Focus on business-critical methods, computations, and edge cases.
5. Can I use PyTest with Odoo.sh or only on self-hosted?
You can use PyTest locally or in your CI/CD pipelines. Odoo.sh uses its own runner, but with custom steps, you can integrate PyTest as well.