Creates comprehensive test suites for Odoo 16.0 modules following Siafa project standards. This skill should be used when creating tests for Odoo modules, such as "Create tests for this module" or "Generate test cases for stock_location_usage_restriction" or "Add unit tests to validate this functionality". The skill provides test templates, patterns, and best practices specific to Odoo 16.0 Enterprise with knowledge of database constraints and common pitfalls in the Siafa codebase.
Creates comprehensive test suites for Odoo 16.0 modules following Siafa project standards.
/plugin marketplace add jamshu/jamshi-marketplace/plugin install odoo-dev@jamshi-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/test_model_basic.pyassets/test_model_constraints.pyassets/test_model_inheritance.pyreferences/test_patterns.mdCreate production-ready test suites for Odoo 16.0 Enterprise modules that follow Siafa project standards, handle database constraints properly, and provide comprehensive test coverage.
Use this skill when:
Examine the module to understand what needs testing:
Identify Components to Test:
Review Module Dependencies:
__manifest__.py for dependenciesCheck for Special Requirements:
Create the test file following Siafa standards:
# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError, ValidationError
class TestModuleName(TransactionCase):
"""Test cases for module_name functionality."""
def setUp(self):
"""Set up test data."""
super().setUp()
# Initialize model references
self.Model = self.env['model.name']
# Set up test data (Step 3)
Critical Import Pattern:
from odoo.tests.common import TransactionCasefrom odoo.tests import TransactionCaseUse the appropriate pattern based on database constraints:
Avoid database constraint issues by using existing records:
def setUp(self):
super().setUp()
self.Model = self.env['model.name']
# Use existing records from database
self.warehouse = self.env['stock.warehouse'].search([], limit=1)
if not self.warehouse:
self.skipTest("No warehouse available for testing")
self.product = self.env['product.product'].search([('type', '=', 'product')], limit=1)
if not self.product:
self.skipTest("No storable product available for testing")
self.partner = self.env['res.partner'].search([], limit=1)
if not self.partner:
self.skipTest("No partner available for testing")
When to use: For models with complex database constraints (products, partners, companies).
Create new records when specific test data is required:
def setUp(self):
super().setUp()
self.Model = self.env['model.name']
# Create test data with .sudo() to bypass permissions
self.vendor = self.env['res.partner'].sudo().create({
'name': 'Test Vendor',
'is_company': True,
'supplier_rank': 1,
})
self.product = self.env['product.product'].sudo().create({
'name': 'Test Product',
'type': 'product',
'purchase_method': 'receive',
'list_price': 100.0,
'standard_price': 80.0,
})
When to use: When specific field values are required for tests or existing records may not have the right attributes.
Use setUpClass for data shared across all test methods:
@classmethod
def setUpClass(cls):
"""Set up test data shared across all test methods."""
super().setUpClass()
cls.vendor = cls.env['res.partner'].sudo().create({
'name': 'Test Vendor',
'is_company': True,
})
When to use: For immutable test data that doesn't change between tests (saves database operations).
Create test methods following these guidelines:
def test_01_descriptive_name(self):
"""Test description in docstring."""
pass
def test_02_another_scenario(self):
"""Test another scenario."""
pass
Numbering: Use 01, 02, etc. to control execution order.
Create tests for each component identified in Step 1:
A. CRUD Operations
def test_01_create_record(self):
"""Test creating a new record with valid data."""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
self.assertTrue(record)
self.assertEqual(record.name, 'Test Record')
self.assertEqual(record.state, 'draft')
def test_02_update_record(self):
"""Test updating an existing record."""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
record.write({'name': 'Updated Record'})
self.assertEqual(record.name, 'Updated Record')
B. Computed Fields
def test_03_computed_field(self):
"""Test computed field calculation."""
record = self.Model.create({
'name': 'Test Record',
'quantity': 10,
'unit_price': 5.0,
})
self.assertEqual(record.total_amount, 50.0)
# Test recomputation on dependency change
record.write({'quantity': 20})
self.assertEqual(record.total_amount, 100.0)
C. Constraints
def test_04_constraint_validation(self):
"""Test constraint prevents invalid data."""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
with self.assertRaises(ValidationError) as context:
record.write({'amount': -10.0})
self.assertIn('must be positive', str(context.exception).lower())
D. Onchange Methods
def test_05_onchange_method(self):
"""Test onchange method updates dependent fields."""
record = self.Model.new({
'name': 'Test Record',
})
record.partner_id = self.partner
record._onchange_partner_id()
# Verify onchange updated related fields
# self.assertEqual(record.expected_field, expected_value)
E. State Transitions
def test_06_state_transition(self):
"""Test state transition workflow."""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
self.assertEqual(record.state, 'draft')
record.action_confirm()
self.assertEqual(record.state, 'confirmed')
# Test invalid transition
with self.assertRaises(UserError) as context:
record.action_confirm() # Already confirmed
self.assertIn('Cannot confirm', str(context.exception))
F. Inheritance/Extension Tests
For modules that inherit existing models:
def test_07_inherited_method_override(self):
"""Test overridden method applies custom logic."""
location = self.Location.create({
'name': 'Test Location',
'usage': 'internal',
'location_id': self.parent_location.id,
})
# Create stock move using this location
self.StockMove.create({
'name': 'Test Move',
'product_id': self.product.id,
'product_uom_qty': 10,
'product_uom': self.product.uom_id.id,
'location_id': location.id,
'location_dest_id': self.parent_location.id,
})
# Test that custom validation prevents usage change
with self.assertRaises(UserError) as context:
location.write({'usage': 'inventory'})
self.assertIn('Cannot change the usage type', str(context.exception))
Apply fixes for known issues in the Siafa codebase:
Problem: Creating products fails with "null value in column 'sale_line_warn' violates not-null constraint"
Solution: Use existing products:
self.product = self.env['product.product'].search([('type', '=', 'product')], limit=1)
Problem: HTML fields return Markup objects: Markup('<p>Text</p>') != 'Text'
Solution: Use non-HTML fields or convert to string:
# Instead of comment field
self.assertEqual(record.barcode, 'TEST001')
# Or convert to string
self.assertIn('expected text', str(record.html_field))
Problem: Tests fail with access rights errors.
Solution: Use .sudo() when creating test data:
self.partner = self.env['res.partner'].sudo().create({...})
Problem: Using old-style super(ClassName, self).setUp()
Solution: Use modern syntax:
super().setUp() # ✅ Correct
Execute tests and verify results:
# Run tests during module update
python3 src/odoo-bin -c src/odoo.conf -d DATABASE_NAME \
--test-enable --stop-after-init \
-u MODULE_NAME
# Run with verbose output
python3 src/odoo-bin -c src/odoo.conf -d DATABASE_NAME \
--test-enable --stop-after-init \
--log-level=test \
-u MODULE_NAME
Expected Output:
INFO MODULE_NAME: 0 failed, 0 error(s) of N tests when loading database 'DATABASE_NAME'
If tests fail:
Add comprehensive docstrings to each test method:
def test_prevent_usage_change_with_moves(self):
"""
Test that location usage cannot be changed when moves exist.
This test verifies that the module prevents changing a location's
usage type after it has been used in stock movements, protecting
data integrity.
"""
# Test implementation
Comprehensive documentation of:
Load this reference when:
Template for testing basic model operations:
Use as starting point for new model tests.
Template for testing constraints:
Use when module has complex validation logic.
Template for testing model inheritance and extensions:
Use when module extends existing Odoo models.
For reference, see /Users/jamshid/PycharmProjects/Siafa/odoo16e_simc/addons-stock/stock_location_usage_restriction/tests/test_stock_location_usage_restriction.py
This file demonstrates:
from odoo.tests.common import TransactionCase)self.product = self.Product.search(...))super().setUp())This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.