1 Overriding tests
Matt Marcha edited this page 2025-11-05 09:59:56 -10:00

Odoo : Overriding native tests

Based on information founded here - Adapted to fit in one file, works in Odoo 15

Inheriting the Class

Identify the class with tests to override and declare a class inheriting it, in the tests folder of your module

# file : tests/test_to_override.py

# -*- coding: utf-8 -*-

from odoo.addons.module.tests.test_to_override import TestToOverride
from odoo.tests import tagged

@tagged('whatever')
class TestToOverrideInherited(TestToOverride):

    def test_function_overriding(self):
        #testing

Disable native tests

Even with this system, native tests will still be ran. To disable them, you need to void the functions :

import unittest

@tagged('whatever')
class TestToOverrideInherited(TestToOverride):
    # Define an easy way to void
    @unittest.skip('Overriden test')
    def void(self: TestToOverride):
        pass

    # disable tests - do not disable a test without writing another one, testing is important!
    TestToOverride.test_function_to_override = void

Javascript tests

Tours

Tours are tests involving Python + JS, supposed to simulate a whole workflow. The JavaScript part is called from the Python tests with the name registered in the JS registry. To override these JS tests, find the file containing the test, and create a file with a similar name and path in your module static directory.

Include this new file through your module's manifest :

# [...]
'assets': {
        'web.assets_tests': [
        '{module}static/path/to/test_file.js',
]}
# [...]

Then, in the file, target the test using registry and patch it. Inside the patch, you can access the original test values using super

/** @odoo-module **/

import { registry } from "@web/core/registry";
import { patch } from "@web/core/utils/patch";
// an example of test to overwrite
import '@stock_barcode/../tests/tours/tour_test_barcode_flows_picking'; 
patch(registry.category("web_tour.tours").get('test_picking_type_mandatory_scan_complete_flux_receipt'), {
    steps() {
	    // We want to update each trigger using a product without barcode by one using auto generated barcode
        const originalSteps = super.steps();
        originalSteps.forEach(function(step, index) {
            originalSteps[index].trigger = step.trigger.replace(':not\(\[data-barcode]\)', '[data-barcode^="PACBC"]')
        });
        
        // You can also add/remove steps using splice
		originalSteps.splice(5, 1, {
            trigger: '.o_scan_message.o_scan_product',
            run: function () {
                const [ lineProductNoBarcode, lineProduct1 ] = helper.getLines();
                helper.assert(
                    lineProduct1.querySelector('.btn.o_edit').disabled, true,
                    "Edit button should be disabled");
                    );
				},
		});
        
        const barcode = lineProductNoBarcode.getAttribute('data-barcode')
        const stepIndex = originalSteps.findIndex((step) => step.trigger === ".o_barcode_line .btn.o_add_remaining_quantity");
        originalSteps.splice(stepIndex, 0, {
            trigger: '.o_barcode_client_action',
            run: 'scan '+barcode,
        });

		// Don't forget to return the overwritten values
        return originalSteps;

    }
});