z0bug-odoo


Namez0bug-odoo JSON
Version 2.0.19 PyPI version JSON
download
home_pagehttps://github.com/zeroincombenze/tools
SummaryOdoo testing framework
upload_time2024-05-14 16:53:01
maintainerNone
docs_urlNone
authorAntonio Maria Vigliotti
requires_pythonNone
licenseAffero GPL
keywords unit test debug
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            =================
z0bug_odoo 2.0.19
=================



|Maturity| |license gpl|



Overview
========

This package is an plug-in of **zerobug** package and aim to easily create odoo tests.

It can be used replacing OCA MQT with some nice additional features.

*z0bug_odoo* is built on follow concepts:

* Odoo version independent; it can test Odoo from 6.1 until 17.0
* It is designed to run inside repository tests with `local travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_
* It is designed to run in local environment too, using `zerobug <https://github.com/zeroincombenze/tools/tree/master/zerobug>`_
* It can run with full or reduced set of pylint tests
* Tests can use many ready-made database records
* Quality Check Id
* Keep database after tests (not inside travis and with some limitations)


travis ci support
-----------------

The goal of z0bug_odoo is to provide helpers to ensure the quality of Odoo addons.
This package can e used replacing OCA MQT and it differs by:

* z0bug_odoo can also test Odoo 6.1 and 7.0 where OCA MQT fails with these versions
* z0bug_odoo can also test Odoo using python2 where OCA abandoned the developing of Odoo base on python2
* z0bug_odoo is designed to execute some debug statements, mainly in local environment
* z0bug_odoo has more options to run with reduced set of lint tests
* OCA MQT is the only component to build environment and test Odoo while z0bug_odoo is part of `Zeroincombenze® tools <https://github.com/zeroincombenze/tools>`_
* As per prior rule, building test environment is made by `vem <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/python_plus>`_, `clodoo <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/clodoo>`_ and `lisa <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/lisa>`_. These commands can also build a complete Odoo environment out of the box

To make a complete test on TravisCI your project following 3 files are required:

* .travis.yml
* requirements.txt
* oca_dependencies.txt


File .travis.yml
~~~~~~~~~~~~~~~~

In order to setup TravisCI continuous integration for your project, just copy the
content of the `sample_files <https://github.com/zeroincombenze/tools/tree/master/travis_emulator/template_travis.yml>`_
to your project’s root directory.

Then execute the command:

::

    make_travis_conf <TOOLS_PATH>/travis_emulator/template_travis.yml .travis.yml

You can check travis syntax with the `lint checker <http://lint.travis-ci.org/>`_ of travis, if available.

Notice: if you do not use travisCi web site, you can avoid to set .travis.yml file.
Local travis emulator and z0bug_odoo create local .travis.yml dinamically.


Odoo test integration
~~~~~~~~~~~~~~~~~~~~~

Current Odoo project version is declared by **VERSION** variable.
If your Odoo module must be tested against Odoo core,
you can test specific github repository by **ODOO_REPO** variable.
You can test against:

* odoo/odoo
* OCA/OCB
* zeroincombenze/OCB

You can test against specific Odoo core version with ODOO_BRANCH variable if differs from your project version:

::

    # Odoo Branch 16.0
    - VERSION="16.0" ODOO_REPO="odoo/odoo"

    # Pull request odoo/odoo#143 (not in local)
    -  VERSION="pull/143" ODOO_REPO="OCA/OCB"

    # Branch saas-17
    - ODOO_REPO="odoo/odoo" ODOO_BRANCH="saas-17"


OCB / core test
~~~~~~~~~~~~~~~

Zeroincombenze® OCB uses submodules. When test is starting, travis-ci upgrades repository and submodules.
To avoid submodules upgrade use this directive compatible with OCA MQT:

::

    - git:
      submodules: false

z0bg_odoo set security environment. You do not need to add any security statements.
You can avoid the following OCA MQT directive:

::

    - pip install urllib3[secure] --upgrade; true

z0bg_odoo does some code upgrade.
You can avoid following directive in ODOO_TEST_SELECT="APPLICATIONS":

::

    - sed -i "s/self.url_open(url)/self.url_open(url, timeout=100)/g" ${TRAVIS_BUILD_DIR}/addons/website/tests/test_crawl.py;

You can avoid following directive in ODOO_TEST_SELECT="LOCALIZATION":

::

    - sed -i "/'_auto_install_l10n'/d" ${TRAVIS_BUILD_DIR}/addons/account/__manifest__.py


Python version
~~~~~~~~~~~~~~

Odoo version from 6.1 to 10.0 are tested with python 2.7
From Odoo 11.0, python3 is used. You can test against 3.6, 3.7, 3.8, 3.9 and 3.10 python versions.
Python 3.5 still works but support is ended.
This is the declaration:

::

    python:
      - "3.6"       # Odoo 11.0 12.0
      - "3.7"       # Odoo 12.0
      - "3.8"       # Odoo 13.0 14.0
      - "3.9"       # Odoo 15.0 16.0
      - "3.10"      # Odoo 17.0

.. note::

    python 3.5 support is ended on 2020 and 3,6 is ended on 2021.

.. warning::

    Currently, some Odoo version cannot support python 3.8+. See above.


Deployment and setup environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In order to deploy test environment and setup code you have to declare some .travis.yml directives divides in following 3 parts:

* Linux packages needed
* PYPI packages
* Odoo repositories dependencies

Linux packages must be declared in ``<addons/apt>`` section of .travis.yml using Ubuntu namespace.
If you run test in local environment, travis emulator automatically translate Ubuntu names into your local distro names, if necessary.
See `travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_ guide for furthermore info.

The PYPI packages, installable by PIP are declared in standard PIP way, using **requirements.txt** file.

If your project depends on other Odoo Github repositories like OCA, create a file called **oca_dependencies.txt** at the root of your project and list the dependencies there.
One per line like so:

::

    project_name optional_repository_url optional_branch_name

During testbed setup, z0bug_odoo will automatically download and place these repositories accordingly into the addon path.
Note on addons path ordering: they will be placed after your own repo, but before the odoo core repo.

If missed optional_repository_url, the repository is searched for repository with the same owner of tested project.

.. note::

    This behaviour differs from OCA MQT

OCA MQT always loads OCA repository while z0bug_odoo searches for current owner repository.
So you will test both with z0bug_odoo and both OCA MQT, always insert the full repository URL.

Test execution
~~~~~~~~~~~~~~

Tests run by travis_run_test command. The script is deployed in _travis directory of **zerobug** package.
Command have to be in ``<script>`` section of .travis.yml file:

::

    script:
        - travis_run_tests


Isolated pylint+flake8 checks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to make a build for these checks, you can add a line
on the ``<env>`` section of the .travis.yml file with this content:

::

    - VERSION="12.0" LINT_CHECK="1"

To avoid making again these checks on other builds, you have to add
LINT_CHECK="0" variable on the line:

::

    - VERSION="12.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0"

You can superset above options in local travis emulator.


Reduced set of lint check
~~~~~~~~~~~~~~~~~~~~~~~~~

You can execute reduced set of check, in order to gradually evolve your code quality
when you meet too many errors.

To enable reduced set of check add one of follow lines:

::

    - LINT_CHECK="1" LINT_CHECK_LEVEL="MINIMAL"
    - LINT_CHECK="1" LINT_CHECK_LEVEL="REDUCED"
    - LINT_CHECK="1" LINT_CHECK_LEVEL="AVERAGE"
    - LINT_CHECK="1" LINT_CHECK_LEVEL="NEARBY"
    - LINT_CHECK="1" LINT_CHECK_LEVEL="OCA"

Odoo core has internal pylint test that checks for all modules even the dependecies.
So if some dependecies module does not meet this test, then the full travis test fails without testing the target repository.

Please, add test_lint to EXCLUDE variable to avoid this fail-over. See below for furthermore informations.

Look at follow table to understand which tests are disabled at specific level:

FLAKE8 (see http://flake8.pycqa.org/en/latest/user/error-codes.html for deatils)

+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| Test | MINIMAL    | REDUCED    | AVERAGE | NEARBY | OCA        | Note                                                                                                                             |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E117 | |no_check| | |no_check| |         |        | |no_check| | over-indented                                                                                                                    |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E121 | |no_check| | |no_check| |         |        | |no_check| | `continuation line under-indented for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E121.html>`_                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E123 | |no_check| | |no_check| |         |        | |no_check| | `Closing bracket does not match indentation of opening bracket's line <https://lintlyci.github.io/Flake8Rules/rules/E123.html>`_ |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E124 | |no_check| | |no_check| |         |        | |check|    | `Closing bracket does not match visual indentation <https://lintlyci.github.io/Flake8Rules/rules/E124.html>`_                    |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E126 | |no_check| | |no_check| |         |        | |check|    | `Continuation line over-indented for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E126.html>`_                   |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E127 | |no_check| | |no_check| |         |        | |check|    | `continuation line over-indented for visual indent <https://lintlyci.github.io/Flake8Rules/rules/E127.html>`_                    |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E128 | |no_check| | |no_check| |         |        | |check|    | `Continuation line under-indented for visual indent <https://lintlyci.github.io/Flake8Rules/rules/E128.html>`_                   |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E131 | |no_check| | |no_check| |         |        | |no_check| | `continuation line unaligned for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E131.html>`_                       |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E133 | |no_check| | |no_check| |         |        | |no_check| | `Closing bracket is missing indentation <https://lintlyci.github.io/Flake8Rules/rules/E133.html>`_                               |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E201 | |no_check| | |check|    |         |        | |check|    | `Whitespace after '(' <https://lintlyci.github.io/Flake8Rules/rules/E201.html>`_                                                 |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E202 | |no_check| | |check|    |         |        | |check|    | `Whitespace before ')' <https://lintlyci.github.io/Flake8Rules/rules/E202.html>`_                                                |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E203 | |no_check| | |check|    |         |        | |check|    | `Whitespace before ':' <https://lintlyci.github.io/Flake8Rules/rules/E203.html>`_                                                |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E211 | |no_check| | |check|    |         |        | |check|    | `whitespace before '(' <https://lintlyci.github.io/Flake8Rules/rules/E211.html>`_                                                |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E221 | |no_check| | |check|    |         |        | |check|    | `Multiple spaces before operator <https://lintlyci.github.io/Flake8Rules/rules/E221.html>`_                                      |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E222 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E225 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E226 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E231 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E241 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E242 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E251 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E261 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E262 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E265 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E266 | |no_check| | |no_check| |         |        | |check|    | `too many leading '#' for block comment <https://lintlyci.github.io/Flake8Rules/rules/E266.html>`_                               |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E271 | |no_check| | |no_check| |         |        | |check|    | `multiple spaces after keyword <https://lintlyci.github.io/Flake8Rules/rules/E271.html>`_                                        |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E272 | |no_check| | |no_check| |         |        | |check|    | `multiple spaces before keyword <https://lintlyci.github.io/Flake8Rules/rules/E272.html>`_                                       |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W291 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W292 | |no_check| | |no_check| |         |        | |check|    | `no newline at end of file <https://lintlyci.github.io/Flake8Rules/rules/W292.html>`_                                            |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W293 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E301 | |no_check| | |no_check| |         |        | |check|    | `Expected 1 blank line <https://lintlyci.github.io/Flake8Rules/rules/E301.html>`_                                                |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E302 | |no_check| | |no_check| |         |        | |check|    | No __init__.py                                                                                                                   |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E303 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E305 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W391 | |no_check| | |no_check| |         |        | |check|    | blank line at end of file                                                                                                        |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| F401 | |no_check| | |check|    |         |        | |no_check| | module imported but unused                                                                                                       |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E501 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E502 | |no_check| | |no_check| |         |        | |check|    | `the backslash is redundant between brackets <https://lintlyci.github.io/Flake8Rules/rules/E502.html>`_                          |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W503 | |no_check| | |no_check| |         |        | |no_check| | No __init__.py                                                                                                                   |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| W504 | |no_check| | |no_check| |         |        | |no_check| | No __init__.py                                                                                                                   |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| F601 | |no_check| | |no_check| |         |        | |no_check| | dictionary key name repeated with different values                                                                               |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E701 | |no_check| | |no_check| |         |        | |check|    | multiple statements on one line (colon)                                                                                          |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| E722 | |no_check| | |no_check| |         |        | |check|    | do not use bare except                                                                                                           |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| F811 | |no_check| | |no_check| |         |        | |no_check| | redefinition of unused name from line N (No __init__.py)                                                                         |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+
| F841 | |no_check| | |no_check| |         |        | |no_check| | `local variable 'context' is assigned to but never used <https://lintlyci.github.io/Flake8Rules/rules/F841.html>`_               |
+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+




PYLINT (see http://pylint-messages.wikidot.com/all-codes for details)

+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| Test  | MINIMAL    | REDUCED    | AVERAGE | NEARBY | OCA     | Notes                                                                               |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W0101 | |no_check| | |no_check| |         |        | |check| | `unreachable <http://pylint-messages.wikidot.com/messages:w0101>`_                  |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W0312 | |no_check| | |check|    |         |        | |check| | `wrong-tabs-instead-of-spaces <http://pylint-messages.wikidot.com/messages:w0312>`_ |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W0403 | |no_check| | |no_check| |         |        | |check| | relative-import                                                                     |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W1401 | |no_check| | |check|    |         |        | |check| | anomalous-backslash-in-string                                                       |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| E7901 | |no_check| | |no_check| |         |        | |check| | `rst-syntax-error <https://pypi.org/project/pylint-odoo/1.4.0>`_                    |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| C7902 | |no_check| | |check|    |         |        | |check| | missing-readme                                                                      |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7903 | |no_check| | |no_check| |         |        | |check| | javascript-lint                                                                     |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7908 | |no_check| | |no_check| |         |        | |check| | missing-newline-extrafiles                                                          |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7909 | |no_check| | |no_check| |         |        | |check| | redundant-modulename-xml                                                            |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7910 | |no_check| | |check|    |         |        | |check| | wrong-tabs-instead-of-spaces                                                        |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7930 | |no_check| | |no_check| |         |        | |check| | `file-not-used <https://pypi.org/project/pylint-odoo/1.4.0>`_                       |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7935 | |no_check| | |no_check| |         |        | |check| | missing-import-error                                                                |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7940 | |no_check| | |no_check| |         |        | |check| | dangerous-view-replace-wo-priority                                                  |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W7950 | |no_check| | |no_check| |         |        | |check| | odoo-addons-relative-import                                                         |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| E8102 | |no_check| | |check|    |         |        | |check| | invalid-commit                                                                      |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| C8103 | |no_check| | |check|    |         |        | |check| | `manifest-deprecated-key <https://pypi.org/project/pylint-odoo/1.4.0>`_             |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W8103 | |no_check| | |no_check| |         |        | |check| | translation-field                                                                   |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| C8104 | |no_check| | |no_check| |         |        | |check| | `class-camelcase <https://pypi.org/project/pylint-odoo/1.4.0>`_                     |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W8104 | |no_check| | |no_check| |         |        | |check| | api-one-deprecated                                                                  |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| C8105 | |no_check| | |check|    |         |        | |check| | `license-allowed <https://pypi.org/project/pylint-odoo/1.4.0>`_                     |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| C8108 | |no_check| | |no_check| |         |        | |check| | method-compute                                                                      |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| R8110 | |no_check| | |check|    |         |        | |check| | old-api7-method-defined                                                             |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| W8202 | |no_check| | |check|    |         |        | |check| | use-vim-comment                                                                     |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |check|    |         |        | |check| | sql-injection                                                                       |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |check|    |         |        | |check| | duplicate-id-csv                                                                    |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |no_check| |         |        | |check| | create-user-wo-reset-password                                                       |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |no_check| |         |        | |check| | dangerous-view-replace-wo-priority                                                  |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |no_check| |         |        | |check| | translation-required                                                                |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |check|    |         |        | |check| | duplicate-xml-record-id                                                             |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |no_check| |         |        | |check| | no-utf8-coding-comment                                                              |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |check|    |         |        | |check| | attribute-deprecated                                                                |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+
| N/A   | |no_check| | |no_check| |         |        | |check| | consider-merging-classes-inherited                                                  |
+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+




Disable some pylint and/or flake8 checks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can disable some specific test or some file from lint checks.

To disable flake8 checks on specific file you can add following line at the beginning of python file:

::

    # flake8: noqa

To disable pylint checks on specific file you can add following line at the beginning of python file:

::

    # pylint: skip-file

To disable both flake8 and pylint checks on specific file you can add following line at the beginning of python file:

::

    # flake8: noqa - pylint: skip-file

To disable pylint checks on specific XML file you can add following line in XML file after xml declaration:

::

    <!-- pylint:disable=deprecated-data-xml-node -->

You can disable specific flake8 check in some source part of python file adding a comment at the same statement to disable check. Here an example to disable sql error (notice comment must be at beginning of the statement):

::

    from builtins import *  # noqa: F403

If you have to disable more than one error you can add following declaration:

::

    from builtins import *  # noqa

You can also disable specific pylint check in some source part of python file adding a comment at the same statement to disable check. Here an example to disable sql error (notice comment must be at beginning of the statement):

::

    self._cr.execute()      # pylint: disable=E8103


Disable unit test
~~~~~~~~~~~~~~~~~

If you want to make a build without tests, you can use the following directive:
``TEST_ENABLE="0"``

You will simply get the databases with packages installed,
but without running any tests.


Reduced set of unit test
~~~~~~~~~~~~~~~~~~~~~~~~

Odoo modules may fail in Travis CI or in local environment.
Currently Odoo OCB core tests fail; we are investigating for the causes.
However you can use a simple workaround, disabling some test.
Currently tests fail are:

* test_impex
* test_ir_actions
* test_lint
* test_main_flows
* test_search
* test_user_has_group

Example:

::

    - export EXCLUDE=test_impex,test_ir_actions,test_lint,test_main_flows,test_search,test_user_has_group
    - TESTS="1" ODOO_TEST_SELECT="ALL"
    - TESTS="1" ODOO_TEST_SELECT="NO-CORE"
    - ....

You can set parameter local GBL_EXCLUDE to disable these test for all repositories.
You will be warned that local GBL_EXCLUDE has only effect for local emulation.
To avoid these test on web travis-ci you have to set EXCLUDE value in .travis.yml file.

Look at follow table to understand which set of tests are enabled or disabled:

+--------------------+--------------+--------------+--------------+-------------------------+
| statement          | application  | local module | odoo/addons  | addons + dependencies   |
+--------------------+--------------+--------------+--------------+-------------------------+
| ALL                | |check|      | |check|      | |check|      | |check|                 |
+--------------------+--------------+--------------+--------------+-------------------------+
| APPLICATIONS       | |check|      | |no_check|   | |no_check|   | Only if application     |
+--------------------+--------------+--------------+--------------+-------------------------+
| LOCALIZATION       | |no_check|   | |check|      | |no_check|   | Only local modules      |
+--------------------+--------------+--------------+--------------+-------------------------+
| CORE               | |no_check|   | |no_check|   | |check|      | |no_check|              |
+--------------------+--------------+--------------+--------------+-------------------------+
| NO-APPLICATION     | |no_check|   | |check|      | |check|      | No if application       |
+--------------------+--------------+--------------+--------------+-------------------------+
| NO-LOCALIZATION    | |check|      | |no_check|   | |check|      | No local modules        |
+--------------------+--------------+--------------+--------------+-------------------------+
| NO-CORE            | |check|      | |check|      | |no_check|   | |check|                 |
+--------------------+--------------+--------------+--------------+-------------------------+




Dependencies test
~~~~~~~~~~~~~~~~~

Since late Summer 2021, z0bug_odoo checks for dependencies.
This test is a sub test of unit test. This is the directive:

::

    - TESTS="1" TEST_DEPENDENCIES="1"


Module unit tests
~~~~~~~~~~~~~~~~~

z0bug_odoo is also capable to test each module individually.
The intention is to check if all dependencies are correctly defined.
Activate it through the ``UNIT_TEST`` directive.
An additional line should be added to the ``env:`` section,
similar to this one:

::

    - VERSION="12.0" UNIT_TEST="1"


Automatic module translation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since late Summer 2021, z0bug_odoo activate automatic module translation after test ended with success.
This is the directive:

::

    - VERSION="12.0" ODOO_TNLBOT="1"

This feature is still experimental.


Names used for the test databases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

z0bug_odoo has a nice feature of organizing your testing databases.
You might want to do that if you want to double them up as
staging DBs or if you want to work with an advanced set of
templates in order to speed up your CI pipeline.
Just specify at will:

``MQT_TEMPLATE_DB='odoo_template' MQT_TEST_DB='odoo_test'``

In your local travis you can declare the default value but these values are not applied in web TravisCi web site.

Database user is the current username. This behavior works both in local test both in TravisCi web site.
However, sometimes, local user and db username can be different. You can set the default value in travis emulator.


Coveralls/Codecov configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`Coveralls <https://coveralls.io/>`_ and `Codecov <https://codecov.io/>`_ services provide information on the test coverage of your modules.
Currently both configurations are automatic (check default configuration `here <cfg/.coveragerc>`_.
So, as of today, you don't need to include a ``.coveragerc`` into the repository,
If you do it, it will be simply ignored.


Other configurations
~~~~~~~~~~~~~~~~~~~~

You can highly customize you test: look at below table.

+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| variable               | default value                                          | meaning                                                                   |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| CHROME_TEST            |                                                        | Set value to 1 to use chrome client to test                               |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| DATA_DIR               | ~/data_dir                                             | Odoo data directory (data_dir in config file)                             |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| EXCLUDE                |                                                        | Modules to exclude from test                                              |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| INCLUDE                |                                                        | Modules to test (all                                                      |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| INSTALL_OPTIONS        |                                                        | Options passed to odoo-bin/openerp-server to install modules              |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| MQT_DBSUER             | $USER                                                  | Database username                                                         |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| MQT_TEMPLATE_DB        | template_odoo                                          | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| MQT_TEST_DB            | test_odoo                                              | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| NPM_CONFIG_PREFIX      | \$HOME/.npm-global                                     | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| ODOO_COMMIT_TEST       | 0                                                      | Test result will be committed; require specific code at TearDown function |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| ODOO_REPO              | odoo/odoo                                              | OCB repository against test repository                                    |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| ODOO_SETUPS            | __manifest__.py __openerp__.py __odoo__.py __terp__.py | Names of Odoo manifest files                                              |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| ODOO_TEST_SELECT       | ALL                                                    | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| ODOO_TNLBOT            | 0                                                      | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| OPTIONS                |                                                        | Options passed to odoo-bin/openerp-server to execute tests                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PHANTOMJS_VERSION      |                                                        | Version of PhantomJS                                                      |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_TXT_COLOR           | 0;97;40                                                | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_RUN_COLOR           | 1;37;44                                                | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_NOP_COLOR           | 34;107                                                 | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_HDR1_COLOR          | 97;42                                                  | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_HDR2_COLOR          | 30;43                                                  | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PS_HDR3_COLOR          | 30;45                                                  | N/D                                                                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| PYPI_RUN_PYVER         | (2.7|3.5|3.6|3.7|3.8|3.9)                              | python versions to run (only PYPI projects)                               |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| SERVER_EXPECTED_ERRORS |                                                        | # of expected errors after tests                                          |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| TEST_DEPENDENCIES      | 0                                                      | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| TRAVIS_DEBUG_MODE      | 0                                                      | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| TRAVIS_PDB             |                                                        | The value 'true' activates pdb in local 'travis -B'                       |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| UNBUFFER               | 1                                                      | Use unbuffer (colors) to log results                                      |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| UNIT_TEST              |                                                        | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| TEST                   |                                                        | Read above                                                                |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| VERSION                |                                                        | Odoo version to test (see above)                                          |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| WEBSITE_REPO           |                                                        | Load package for website tests                                            |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+
| WKHTMLTOPDF_VERSION    | 0.12.5                                                 | Version of wkhtmltopdf (value are 0.12.1                                  |
+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+





Debug information
~~~~~~~~~~~~~~~~~

If you declare the following directive in <env global> section:

``TRAVIS_DEBUG_MODE="n"``

where "n" means:

+---------------------------+-------------+-------------+-------------+----------+--------------+
| Parameter                 | 0           | 1           | 2           | 3        | 9            |
+---------------------------+-------------+-------------+-------------+----------+--------------+
| Informative messages      | |no_check|  | |check|     | |check|     | |check|  | |check|      |
+---------------------------+-------------+-------------+-------------+----------+--------------+
| Inspect internal data     | |no_check|  | |no_check|  | |check|     | |check|  | |check|      |
+---------------------------+-------------+-------------+-------------+----------+--------------+
| MQT tests                 | |no_check|  | |no_check|  | |no_check|  | |check|  | |check|      |
+---------------------------+-------------+-------------+-------------+----------+--------------+
| Installation log level    | ERROR       | WARN        | INFO        | INFO     | |no_check|   |
+---------------------------+-------------+-------------+-------------+----------+--------------+
| Execution log level       | INFO        | TEST        | TEST        | TEST     | |no_check|   |
+---------------------------+-------------+-------------+-------------+----------+--------------+



Note this feature does not work with OCA MQT. Local test and TravisCI test have slightly different behavior.

When MQT is execute in local environment the value

``TRAVIS_DEBUG_MODE="9"``

does not execute unit test. It is used to debug MQT itself.

See `local travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_


Tree directory
~~~~~~~~~~~~~~

While travis is running this is the tree directory:

::

    ${HOME}                         # home of virtual environment (by TravisCI)
    ┣━━ build                       # build root (by TravisCI)
    ┃    ┣━━ ${TRAVIS_BUILD_DIR}    # testing repository (by TravisCI)
    ┃    ┗━━ ${ODOO_REPO}           # odoo or OCB repository to check with       (0) (1) (2)
    ┃
    ┣━━ ${ODOO_REPO}-${VERSION}     # symlink of ${HOME}/build/{ODOO_REPO}       (0) (1)
    ┃
    ┣━━ dependencies                # Odoo dependencies of repository            (0) (3)
    ┃
    ┣━━ tools                       # clone of Zeroincombenze tools              (3) (4)
    ┃    ┃
    ┃    ┣━━ zerobug                # z0bug testing library
    ┃    ┃       ┗━━ _travis        # testing commands
    ┃    ┗━━ z0bug_odoo             # Odoo testing library
    ┃            ┗━━ travis         # testing commands
    ┃
    ┗━━ maintainer-quality-tools    # OCA testing library
         ┗━━ travis                 # testing commands

    (0) Same behavior of OCA MQT
    (1) Cloned odoo/odoo or OCA/OCB repository to check compatibility of testing modules
    (2) If the testing project is OCB, travis_install_env ignore this directory
    (3) Done by then following statements in .travis.yml:
        - travis_install_env
        Above statements replace the OCA statements:
        - travis_install_nightly
    (4) Done by following statements in .travis.yml::
        - git clone https://github.com/zeroincombenze/tools.git ${HOME}/tools --depth=1
        - \${HOME}/tools/install_tools.sh -qpt
        - source ${HOME}/devel/activate_tools -t
        Above statements replace OCA following statements:
        - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools --depth=1
        - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}

TestEnv: the test environment
-----------------------------

TestEnv makes available a test environment ready to use in order to test your Odoo
module in quick and easy way.

The purpose of this software are:

* Create the Odoo test environment with records to use for your test
* Make available some useful functions to test your module
* Simulate the wizard to test wizard functions (wizard simulator)
* Environment running different Odoo modules versions
* Keep database after tests (with some limitations)

Please, pay attention to test data: TestEnv use internal unicode even for python 2
based Odoo (i.e. 10.0). You should declare unicode date whenever is possible.

.. note::

    Odoo core uses unicode even on old Odoo version.

For a complete set of examples, please look at the module test_testenv in
`repository <https://github.com/zeroincombenze/zerobug-test>`__

Tests are based on test environment created by module mk_test_env in
`repository <https://github.com/zeroincombenze/zerobug-test>`__

keeping database after tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Using zerobug in conjunction with z0bug_odoo a nice feature is available: you can keep
the database after tests, so you can touch teh results or build example DB.
However this feature has some limitation:

    #. You can use just 1 test class, because saving is made on TearDown execution
    #. You cannot create on fly record with external reference of current module name

Example 1, double test class: it does not work

::

    class TestExample(SingleTransactionCase):
        ...

    class Test2Example(SingleTransactionCase):
        ...

Example 2, module named "my_module":

::

    class TestExample(SingleTransactionCase):
        ...
        # Follow record with external reference "my_module.my_xref" will be
        # automaticaaly deleted by Odoo at the end of the test
        self.resource_create("my.model", xref="my_module.my_xref", ...
        # Follow record with external reference "z0bug.my_xref" works!
        self.resource_create("my.model", xref="z0bug.my_xref", ...

Names used for the test databases in testenv
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

zerobug and z0bug_odoo use different rule from travis emulatro to naming test database.
The database name is "test_<MODULE_NAME>".

Please, notice a template database, named "template_<MODULE_NAME>" is built before test
database and then ie kept in the system.
If you do not want to see template databases use following regex for dbfilter parameter
inf your Odoo configuration file:

    dbfilter = (?!template).*

Requirements
~~~~~~~~~~~~

Ths TestEnv software requires:

* python_plus PYPI package
* z0bug_odoo PYPI package 2.0.19
* python 2.7 / 3.6 / 3.7 / 3.8

TestEnv is full integrated with Zeroincombenze® tools.
See `readthedocs <https://zeroincombenze-tools.readthedocs.io/>`__
and `zeroincombenze github <https://github.com/zeroincombenze/tools.git>`__
Zeroincombenze® tools help you to test Odoo module with pycharm.



Features
--------

Data to use in tests are store in csv files in data directory.
File names are tha name of the models (table) with characters '.' (dot) replaced by '_' (underscore)

Header of file must be the names of table fields.

Rows can contains value to store or Odoo external reference or macro.

For type char, text, html, int, float, monetary: value are constants inserted as is.

For type many2one: value may be an integer (record id) or Odoo external reference (format "module.name").

For type data, datetime: value may be a constant or relative date



Usage
=====

Usage Details
-------------

You can locate the recent testenv.py in testenv directory of module
`z0bug_odoo <https://github.com/zeroincombenze/tools/tree/master/z0bug_odoo/testenv>`__

For full documentation visit:
`zero-tools <https://zeroincombenze-tools.readthedocs.io/en/latest/pypi_z0bug_odoo/index.html>`__
or
`z0bug_odoo <https://z0bug-odoo.readthedocs.io/en/latest/>`__
or
`zero-tools (github) <https://github.com/zeroincombenze/tools>`__
or
`github with example modules <https://github.com/zeroincombenze/zerobug-test>`__

Copy the testenv.py file in tests directory of your module.
Please copy the documentation testenv.rst file in your module too.

The __init__.py must import testenv.

::

    from . import testenv
    from . import test_<MY_TEST_FILE>

Your python test file have to contain some following example lines:

::

    import os
    import logging
    from .testenv import MainTest as SingleTransactionCase

    _logger = logging.getLogger(__name__)

    TEST_SETUP_LIST = ["res.partner", ]

    class MyTest(SingleTransactionCase):

        def setUp(self):
            super().setUp()
            # Add following statement just for get debug information
            self.debug_level = 2
            # keep data after tests
            self.odoo_commit_data = True
            self.setup_env()                # Create test environment

        def tearDown(self):
            super().tearDown()

        def test_mytest(self):
            _logger.info(
                "🎺 Testing test_mytest"    # Use unicode char to best log reading
            )
            ...

An important helper to debug is self.debug_level. When you begins your test cycle,
you are hinted to set self.debug_level = 3; then you can decrease the debug level
when you are developing stable tests.
Final code should have self.debug_level = 0.
TestEnv logs debug message with symbol "🐞 " so you can easily recognize them.
Another useful helper is the database keep data after test feature. You have to declare
self.odoo_commit_data = True and you have to set global bash environment

``global ODOO_COMMIT_DATA="1"``

Ths TestEnv software requires:

* python_plus PYPI package
* z0bug_odoo PYPI package version 2.0.19
* python 2.7 / 3.6 / 3.7 / 3.8 / 3.9 / 3.10



Model data declaration
~~~~~~~~~~~~~~~~~~~~~~

Each model is declared in a csv file or xlsx file in **test/data** directory of the
module. The file name is the same of model name with dots replaced by undescore.

i.e. below the contents of **res_parter.csv** file:

::

    id,name,street
    z0bug.partner1,Alpha,"1, First Avenue"

The model may also be declared in a dictionary which key which is the external
reference used to retrieve the record.

i.e. the following record declaration is the same of above example; record id is named
``z0bug.partner1`` in res.partner:

::

    TEST_RES_PARTNER = {
        "z0bug.partner1": {
            "name": "Alpha",
            "street": "1, First Avenue",
            ...
        }
    )

.. warning::

    Please, do not to declare ``product.product`` records: they are automatically
    created as child of ``product.template``. The external reference must contain
    the pattern ``_template`` (see below).

.. warning::

    When you write a file with a spreadsheet app, pay attention to automatic string
    replacement. For example double quote char <"> may be replaced by <”>.
    These replaced characters may be create some troubles during import data step,
    expecially when used in "python expression".



Magic relationship
~~~~~~~~~~~~~~~~~~

Some models/tables should be managed together, i.e. **account.move** and **account.move.line**.
TestEnv manages these models/tables, called header/detail, just as a single object.
When header record is created, all detail lines are created with header.
Odoo standard declaration requires the details data in child reference field with
command *0, 0*.
This method make unreadable the source data. Look at the simple follow example with
usually Odoo declaration way:

::

    sale_order_data = {
        "example.order_1": {
            "partner_id": self.env.ref("base.res_partner_1"),
            "origin": "example",
            ...
            "order_line": [
                (0, 0, {
                    "product_id": self.env.ref("product.product_product_1"),
                    "product_qty": 1,
                    "price_unit": 1.23,}),
                (0, 0, {
                    "product_id": self.env.ref("product.product_product_2"),
                    "product_qty": 2,
                    "price_unit": 2.34,}),
            ]
        }

    }

Now look at the same data in internal declaration by **z0bug_odoo**:

::

    TEST_SALE_ORDER = {
        "example.order_1": {
            "partner_id": "base.res_partner_1",
            "origin": "example",
            ...
        }

    }

    TEST_SALE_ORDER_LINE = {
        "example.order_1_1": {
            "product_id": "product.product_product_1",
            "product_qty": 1,
            "price_unit": 1.23,
        },
        "example.order_1_2": {
            "product_id": "product.product_product_2",
            "product_qty": 2,
            "price_unit": 2.34,
        }
    }

As you can see, the data is easy readable and easy updatable. Please, notice:

#. Sale order lines are declared in specific model **sale.order.line**
#. Record ID **must** begin with header ID, followed by "_" and line ID
#. Reference data do not require ``self.env.ref()``: they are automatically referenced

It is also easy write the csv or xlsx file. This is the example with above data

File **sale_order.csv**

::

    id,partner_id,origin
    example.order_1,base.res_partner_1,example

File **sale_order_line.csv**

::

    id,product_id,product_qty,price_unit
    example.order_1_1,product.product_product_1,1,1.23
    example.order_1_2,product.product_product_2,2,2.34

In your test file you must declare the following statement:

::

    TEST_SETUP_LIST = ["sale.order", "sale.order.line"]

.. warning::

    You must declare header and lines data before create header record

.. note::

    External reference coding is free: however is hinted to use the The 2
    keys reference explained in "External reference" chapter.

Another magic relationship is the **product.template** (product) / **product.product** (variant)
relationship.
Whenever a **product.template** (product) record is created,
Odoo automatically creates one variant (child) record for **product.product**.
If your test module does not need to manage product variants you can avoid to declare
**product.product** data even if this model is used in your test data.

For example, you have to test **sale.order.line** which refers to **product.product**.
You simply declare a **product.template** record with external reference
uses "_template" magic text.

::

    TEST_PRODUCT_TEMPLATE = {
        "z0bug.product_template_1": {
            "name": "Product alpha",
            ...
        }
    )

    ...

    TEST_SALE_ORDER_LINE = {
        "z0bug.order_1_1": {
            "product_id": "z0bug.product_product_1",
            ...
        }
    )



External reference
~~~~~~~~~~~~~~~~~~

Every record tagged by an external reference may be:

    * Ordinary Odoo external reference ``(a)``, format "module.name"
    * Test reference, format "z0bug.name" ``(b)``
    * Key value, format "external.key" ``(c)``
    * 2 keys reference, for header/detail relationship ``(d)``
    * Magic reference for **product.template** / **product.product** ``(e)``

Ordinary Odoo external reference ``(a)`` is a record of **ir.model.data**;
you can see them from Odoo GUI interface.

Test reference ``(b)`` are visible just in the test environment.
They are identified by "z0bug." prefix module name.

External key reference ``(c)`` is identified by "external." prefix followed by
the key value used to retrieve the record.
If key value is an integer it is the record "id".
The field "code" or "name" are used to search record;
for account.tax the "description" field is used.
Please set self.debug_level = 2 (or more) to log these field keys.

The 2 keys reference ``(d)`` needs to address child record inside header record
at 2 level model (header/detail) relationship.
The key MUST BE the same key of the parent record,
plus "_", plus line identifier (usually **sequence** field).
i.e. ``z0bug.move_1_3`` means: line with sequence ``3`` of **account.move.line**
which is child of record ``z0bug.move_1`` of **account.move**.
Please set self.debug_level = 2 (or more) to log these relationships.

For **product.template** (product) you must use '_template' text in reference ``(e)``.
TestEnv inherit **product.product** (variant) external reference
(read above "Magic relationship").

Examples:

::

    TEST_ACCOUNT_ACCOUNT = {
        "z0bug.customer_account": {
            "code": "", ...
        }
        "z0bug.supplier_account": {
            "code": "111100", ...
        }
    )

    ...

    self.resource_edit(
        partner,
        web_changes = [
            ("country_id", "base.it"),       # Odoo external reference (type a)
            ("property_account_receivable_id",
             "z0bug.customer_account"),      # Test reference (type b)
            ("property_account_payable_id",
             "external.111100"),             # External key (type c)
        ],
    )



Module test execution session
-----------------------------

Introduction
~~~~~~~~~~~~

Module test execution workflow should be:

    #. Data declaration, in file .csv or .xlszìx or in source code
    #. Base data creation, in setUp() function
    #. Tests execution
    #. Supplemental data creation, during test execution, by group name

Test data may be managed by one or more data group; if not declared,
"base" group name is used. The "base" group will be created at the setUp()
level: it is the base test data.
Testing function may declare and manage other group data. Look at the
following example:

::

    import os
    import logging
    from .testenv import MainTest as SingleTransactionCase

    _logger = logging.getLogger(__name__)

    TEST_PRODUCT_TEMPLATE = {
        "z0bug.product_template_1": {...}
    }
    TEST_RES_PARTNER = {
        "z0bug.partner1": {...}
    )
    TEST_SETUP_LIST = ["res.partner", "product.template"]

    TEST_SALE_ORDER = {
        "z0bug.order_1": {
            "partner_id": "z0bug.partner1",
            ...
        }
    }
    TEST_SALE_ORDER_LINE = {
        "z0bug.order_1_1": {
            "product_id": "z0bug.product_product_1",
            ...
        }
    )

    class MyTest(SingleTransactionCase):

        def setUp(self):
            super().setUp()
            self.debug_level = 2
            self.setup_env()                # Create base test environment

        def test_something(self):
            # Now add Sale Order data, group "order"
            self.setup_env(group="order", setup_list=["sale.order", "sale.order.line"])

Note the external reference are globals and they are visible from any groups.
After base data is created, the real test session can begin. You can simulate
various situation; the most common are:

    #. Simulate web form create record
    #. Simulate web form update record
    #. Simulate the multi-record windows action
    #. Download any binary data created by test
    #. Engage wizard

.. note::

    You can also create / update record with usually create() / write() Odoo function,
    but they do not really simulate the user behavior because they do not engage the
    onchange methods, they do not load any view and so on.

The real best way to test a create record is like the follow example
based on **res.partner model**:

::

        partner = self.resource_edit(
            resource="res.partner",
            web_changes=[
                ("name", "Adam"),
                ("country_id", "base.us"),
                ...
            ],
        )

You can also simulate the update session, issuing the record:

::

        partner = self.resource_edit(
            resource=partner,
            web_changes=[
                ("name", "Adam Prime"),
                ...
            ],
        )

Look at resource_edit() documentation for furthermore details.

In you test session you should need to test a wizard. This test is very easy
to execute as in the follow example that engage the standard language install
wizard:

::

        # We engage language translation wizard with "it_IT" language
        # see "<ODOO_PATH>/addons/base/module/wizard/base_language_install*"
        _logger.info("🎺 Testing wizard.lang_install()")
        act_windows = self.wizard(
            module="base",
            action_name="action_view_base_language_install",
            default={
                "lang": "it_IT"
                "overwrite": False,
            },
            button_name="lang_install",
        )
        self.assertTrue(
            self.is_action(act_windows),
            "No action returned by language install"
        )
        # Now we test the close message
        self.wizard(
            act_windows=act_windows
        )
        self.assertTrue(
            self.env["res.lang"].search([("code", "=", "it_IT")]),
            "No language %s loaded!" % "it_IT"
        )

Look at wizard() documentation for furthermore details.



Data values
-----------

Data values may be raw data (string, number, dates, etc.) or external reference
or some macro.
You can declare data value on your own but you can discover the full test environment
in https://github.com/zeroincombenze/zerobug-test/mk_test_env/ and get data
from this environment.

.. note::

    The fields **company_id** and **currency_id** may be empty to use default value.
    If you want to issue no value, do not declare column in model file (csv or xlsx).

You can evaluate the field value engaging a simple python expression inside tags like in
following syntax:

    "<?odoo EXPRESSION ?>"

The expression may be a simple python expression with following functions:

+--------------+-----------------------------------------------+-------------------------------------------------+
| function     | description                                   | example                                         |
+--------------+-----------------------------------------------+-------------------------------------------------+
| compute_date | Compute date                                  | <?odoo compute_date('<###-##-##')[0:4] ?>       |
+--------------+-----------------------------------------------+-------------------------------------------------+
| random       | Generate random number from 0.0 to 1.0        | <?odoo int(random() * 1000) ?>                  |
+--------------+-----------------------------------------------+-------------------------------------------------+
| ref          | Odoo reference self.env.ref()                 | <?odoo ref('product.product_product_1') ?>      |
+--------------+-----------------------------------------------+-------------------------------------------------+
| ref[field]   | field of record of external reference         | <?odoo ref('product.product_product_1').name ?> |
+--------------+-----------------------------------------------+-------------------------------------------------+
| ref[field]   | field of record of external reference (brief) | <?odoo product.product_product_1.name ?>        |
+--------------+-----------------------------------------------+-------------------------------------------------+



company_id
~~~~~~~~~~

If value is empty, user company is used.
This behavior is not applied on
**res.users**, **res.partner**, **product.template** and **product.product** models.
For these models you must fill the **company_id** field.

When data is searched by ``resource_search()`` function on every model with company_id,
the **company_id** field is automatically added to search domain, using 'or' between
company_id null and company_id equal to supplied value or current user company.



boolean
~~~~~~~

You can declare boolean value:

* by python boolean False or True
* by integer 0 or 1
* by string "0" or "False" or "1" or "True"

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "supplier": False,
                "customer": "True",
                "is_company": 1,
            }
        }
    )



char / text
~~~~~~~~~~~

Char and Text values are python string; please use unicode whenever is possible
even when you test Odoo 10.0 or less.

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                "name": "Alpha",
                "street": "1, First Avenue"
                # Name of Caserta city
                "city": "<? base.state_it_ce.name ?>",
                # Reference: 'year/123'
                "ref": "<? compute_date('####-##-##')[0:4] + '/123' ?>",
            }
        }
    )



integer / float / monetary
~~~~~~~~~~~~~~~~~~~~~~~~~~

Integer, Floating and Monetary values are python integer or float.
If numeric value is issued as string, it is internally converted
as integer/float.

::


    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "color": 1,
                "credit_limit": 500.0,
                "payment_token_count": "0",
            }
        }
    )



date / datetime
~~~~~~~~~~~~~~~

Date and Datetime value are managed in special way.
They are processed by ``compute_date()`` function (read below).
You can issue a single value or a 2 values list, 1st is the date,
2nd is the reference date.

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "activity_date_deadline": "####-1>-##",    # Next month
                "signup_expiration": "###>-##-##",         # Next year
                "date": -1,                                # Yesterday
                "last_time_entries_checked":
                    [+2, another_date],                    # 2 days after another day
                "message_last_post": "2023-06-26",         # Specific date, ISO format
            }
        }
    )



many2one
~~~~~~~~

You can issue an integer (if you know exactly the ID)
or an external reference. Read above about external reference.

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "country_id": "base.it",                   # Odoo external reference
                "property_account_payable_id":
                    "z0bug.customer_account",              # Test record
                "title": "external.Mister"                 # Record with name=="Mister"
            }
        }
    )



one2many / many2many
~~~~~~~~~~~~~~~~~~~~

The one2many and many2many field may contains one or more ID;
every ID use the same above many2one notation with external reference.
Value may be a string (just 1 value) or a list.

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "bank_ids":
                    [
                        "base.bank_partner_demo",
                        "base_iban.bank_iban_china_export",
                    ],
                "category_id": "base.res_partner_category_0",
            }
        }
    )

.. note::

    You can also use tha classic Odoo syntax with commands:
    You can integrate classic Odoo syntax with **z0bug_odoo external** reference.

* [0, 0, values (dict)]               # CREATE record and link
* [1, ID (int), values (dict)]        # UPDATE linked record
* [2, ID (int)]                       # DELETE linked record by ID
* [3, ID (int)]                       # UNLINK record ID (do not delete record)
* [4, ID (int)]                       # LINK record by ID
* [5, x] or [5]                       # CLEAR unlink all record IDs
* [6, x, IDs (list)]                  # SET link record IDs



binary
~~~~~~

Binary file are supplied with os file name. Test environment load file and
get binary value. File must be located in **tests/data** directory.

::

    self.resource_create(
        "res.partner",
        xref="z0bug.partner1",
        values={
             {
                ...
                "image": "z0bug.partner1.png"
            }
        }
    )



Useful External Reference
-------------------------

+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| id                                     | name                  | model                | note                                           |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| z0bug.coa_bank                         | Bank                  | account.account      | Default bank account                           |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| external.INV                           | Sale journal          | account.journal      | Default sale journal                           |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| external.BILL                          | Purchase journal      | account.journal      | Default purchase journal                       |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| external.MISC                          | Miscellaneous journal | account.journal      | Default miscellaneous journal                  |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| external.BNK1                          | Bank journal          | account.journal      | Default bank journal                           |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| account.account_payment_term_immediate | Immediate Payment     | account.payment.term |                                                |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| account.account_payment_term_net       | 30 Net Days           | account.payment.term |                                                |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| z0bug.tax_22a                          | Purchase 22% VAT      | account.tax          | Italian default purchase VAT rate              |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| z0bug.tax_22v                          | Sale 22% VAT          | account.tax          | Italian default sale VAT rate                  |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| base.main_company                      | Default company       | res.company          | Default company for test                       |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| product.product_category_1             | All / Saleable        | product.category     | Useful product category                        |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| base.USD                               | USD currency          | res.currency         | Test currency during test execution: US dollar |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+
| base.user_root                         | Administrator         | res.users            | User under test execution                      |
+----------------------------------------+-----------------------+----------------------+------------------------------------------------+



Functions
---------

cast_types
~~~~~~~~~~

**cast_types(self, resource, values, fmt=None, group=None, not_null=False)**

Convert resource fields in appropriate type, based on Odoo type.

| Args:
|     resource (str): Odoo model name
|     values (dict): record data
|     fmt (selection): output format
|     group (str): used to manager group data; default is "base"
|
| Returns:
|     Appropriate values

The parameter fmt declares the purpose of casting and declare the returned format of
<2many> fields as follows table:

::

                                    | fmt=='cmd'         | fmt=='id'  | fmt=='py'
    <2many> [(0|1,x,dict)]          | [(0|1,x,dict)] *   | [dict] *   | [dict] *
    <2many> [(0|1,x,xref)]          | [(0|1,x,dict)]     | [dict]     | [dict]
    <2many> [(2|3|4|5,id)]          | as is              | as is      | as is
    <2many> [(2|3|4|5,xref)]        | [(2|3|4|5,id)]     | as is      | as is
    <2many> [(6,0,[ids])]           | as is              | [ids]      | [ids]
    <2many> [(6,0,xref)]            | [(6,0,[id])]       | [id]       | [id]
    <2many> [(6,0,[xref,...])]      | [(6,0,[ids])]      | [ids]      | [ids]
    <2many> dict                    | [(0,0,dict)        | [dict]     | [dict]
    <2many> xref (exists)           | [(6,0,[id])]       | [id]       | [id]
    <2many> xref (not exists)       | [(0,0,dict)]       | [dict]     | [dict]
    <2many> [xref] (exists)         | [(6,0,[id])]       | [id]       | [id]
    <2many> [xref] (not exists)     | [(0,0,dict)]       | [dict]     | [dict]
    <2many> [xref,...] (exists)     | [(6,0,[ids])]      | [ids]      | [ids]
    <2many> [xref,...] (not exists) | [(0,0,dict),(...)] | [dict,...] | [dict,...]
    <2many> [ids] **                | [(6,0,[ids])]      | [ids]      | [ids]
    <2many> id                      | [(6,0,[id])]       | [id]       | [id]
    <2many> "xref,..." (exists)     | [(6,0,[ids])]      | [ids]      | [ids]
    <2many> "xref,..." (not exists) | [(0,0,dict),(...)] | [dict,...] | [dict,...]

    Caption: dict -> {'a': 'A', ..}, xref -> "abc.def", id -> 10, ids -> 1,2,...
    * fields of dict are recursively processed
    ** ids 1-6 have processed as Odoo cmd

fmt ==  'cmd' means convert to Odoo API format: <2many> fields are returned with
prefixed 0|1|2|3|4|5|6 value (read _cast_2many docs).

fmt == 'id' is like 'cmd': prefix are added inside dict not at the beginning.

fmt == 'py' means convert to native python (remove all Odoo command prefixes).
It is used for comparison.

When no format is required (fmt is None), some conversion may be not applicable:

<many2one> field will be left unchanged when invalid xref is issued and <2many>
field me will be left unchanged when one or more invalid xref are issued.

str, int, long, selection, binary, html fields are always left as is

date, datetime fields and fmt=='cmd' and python2 (odoo <= 10.0) return ISO format
many2one fields, if value is (int|long) are left as is; if value is (xref) the
id of xref is returned.

.. note::

    Odoo one2many valid cmd are: 0,1 and 2 (not checked)

store_resource_data
~~~~~~~~~~~~~~~~~~~

**store_resource_data(self, resource, xref, values, group=None, name=None)**

Store a record data definition for furthermore use.

| Args:
|     resource (str): Odoo model name
|     xref (str): external reference
|     values (dict): record data
|     group (str): used to manager group data; default is "base"
|     name (str): label of dataset; default is resource name


Data stored is used by ``setup_env()`` function and/or by:

* ``resource_create()`` without values
* ``resource_write()`` without values
* ``resource_make()`` without values


compute_date
~~~~~~~~~~~~

**compute_date(self, date, refdate=None)**

Compute date or datetime against today or a reference date.

| Args:
|     date (date or string or integer): text date formula
|     refdate (date or string): reference date

Date may be:

* python date/datetime value
* string with ISO format "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS"
* string value that is a relative date against today or reference date

Relative string format is like ISO, with 3 groups separated by '-' (dash).
Every group may be an integer or a special notation:

* starting with '<' meas subtract; i.e. '<2' means minus 2
* ending with '>' meas add; i.e. '2>' means plus 2
* '#' with '<' or '>' means 1; i.e. '<###' means minus 1
* all '#' means same value of reference date

A special notation '+N' and '-N', where N is an integer means add N days
or subtract N day from reference date.
Here, in following examples, are used python iso date convention:

* '+N': return date + N days to refdate (python timedelta)
* '-N': return date - N days from refdate (python timedelta)
* '%Y-%m-%d': strftime of issued value
* '%Y-%m-%dT%H:%M:%S': same datetime
* '%Y-%m-%d %H:%M:%S': same datetime
* '####-%m-%d': year from refdate (or today), month '%m', day '%d'
* '####-##-%d': year and month from refdate (or today), day '%d'
* '2024-##-##': year 2024, month and day from refdate (or today)
* '<###-%m-%d': year -1  from refdate (or today), month '%m', day '%d'
* '<001-%m-%d': year -1  from refdate (or today), month '%m', day '%d'
* '<###-#>-%d': year -1  from refdate, month +1 from refdate, day '%d'
* '<005-2>-##': year -5, month +2 and day from refdate

Notes:
    * Returns a ISO format string.
    * Returned date is a valid date; i.e. '####-#>-31', with ref month January result '####-02-31' becomes '####-03-03'
    * To force last day of month, set '99': i.e. '####-<#-99' becomes the last day of previous month of refdate


resource_browse
~~~~~~~~~~~~~~~

**resource_browse(self, xref, raise_if_not_found=True, resource=None, group=None)**

Bind record by xref, searching it or browsing it.
This function returns a record using issued parameters. It works in follow ways:

* With valid xref it work exactly like self.env.ref()
* If xref is an integer it works exactly like self.browse()
* I xref is invalid, xref is used to search record
    * xref is searched in stored data
    * xref ("MODULE.NAME"): if MODULE == "external", NAME is the record key

| Args:
|     xref (str): external reference
|     raise_if_not_found (bool): raise exception if xref not found or
|                                if more records found
|     resource (str): Odoo model name, i.e. "res.partner"
|     group (str): used to manager group data; default is "base"
|
| Returns:
|     obj: the Odoo model record
|
| Raises:
|     ValueError: if invalid parameters issued

resource_create
~~~~~~~~~~~~~~~

Create a test record and set external ID to next tests.
This function works as standard Odoo create() with follow improvements:

* It can create external reference too
* It can use stored data if no values supplied
* Use new api even on Odoo 7.0 or less

| Args:
|     resource (str): Odoo model name, i.e. "res.partner"
|     values (dict): record data (default stored data)
|     xref (str): external reference to create
|     group (str): used to manager group data; default is "base"
|
| Returns:
|     obj: the Odoo model record, if created


resource_write
~~~~~~~~~~~~~~

Update a test record.
This function works as standard Odoo write() with follow improvements:

* If resource is a record, xref is ignored (it should be None)
* It resource is a string, xref must be a valid xref or an integer
* If values is not supplied, record is restored to stored data values

def resource_write(self, resource, xref=None, values=None, raise_if_not_found=True, group=None):

    Args:
        resource (str|obj): Odoo model name or record to update
        xref (str): external reference to update: required id resource is string
        values (dict): record data (default stored data)
        raise_if_not_found (bool): raise exception if xref not found or if more records found
        group (str): used to manager group data; default is "base"

    Returns:
        obj: the Odoo model record

    Raises:
        ValueError: if invalid parameters issued

resource_make
~~~~~~~~~~~~~

Create or write a test record.
This function is a hook to resource_write() or resource_create().

def resource_make(self, resource, xref, values=None, group=None):

declare_resource_data
~~~~~~~~~~~~~~~~~~~~~

Declare data to load on setup_env().

| Args:
|     resource (str): Odoo model name, i.e. "res.partner"
|     data (dict): record data
|     name (str): label of dataset; default is resource name
|     group (str): used to manager group data; default is "base"
|     merge (str): values are ("local"|"zerobug")
|
| Raises:
|     TypeError: if invalid parameters issued

declare_all_data
~~~~~~~~~~~~~~~~

Declare all data to load on setup_env()

| Args:
|     message (dict): data message
|         TEST_SETUP_LIST (list): resource list to load
|         TEST_* (dict): resource data; * is the uppercase resource name where
|                        dot are replaced by "_"; (see declare_resource_data)
|     group (str): used to manager group data; default is "base"
|     merge (str): values are ("local"|"zerobug")
|     data_dir (str): data directory, default is "tests/data"
|
| Raises:
|     TypeError: if invalid parameters issued

get_resource_data
~~~~~~~~~~~~~~~~~

Get declared resource data; may be used to test compare

| Args:
|     resource (str): Odoo model name or name assigned, i.e. "res.partner"
|     xref (str): external reference
|     group (str): if supplied select specific group data; default is "base"
|     try_again (bool): engage conveyed value
|
| Returns:
|     dictionary with data or empty dictionary

get_resource_data_list
~~~~~~~~~~~~~~~~~~~~~~

Get declared resource data list.

def get_resource_data_list(self, resource, group=None):

    Args:
        resource (str): Odoo model name or name assigned, i.e. "res.partner"
        group (str): if supplied select specific group data; default is "base"

    Returns:
        list of data

get_resource_list
~~~~~~~~~~~~~~~~~

Get declared resource list.

def get_resource_list(self, group=None):

    Args:
        group (str): if supplied select specific group data; default is "base"

setup_company
~~~~~~~~~~~~~

Setup company values for current user.

This function assigns company to current user and / or can create xref aliases
and /or can update company values.
This function is useful in multi companies tests where different company values
will be used in different tests. May be used in more simple test where company
data will be updated in different tests.
You can assign partner_xref to company base by group; then all tests executed
after setup_env(), use the assigned partner data for company of the group.
You can also create more companies and assign one of them to test by group.

| Args:
|     company (obj): company to update; if not supplied a new company is created
|     xref (str): external reference or alias for main company
|     partner_xref (str): external reference or alias for main company partner
|     recv_xref (str): external reference or alias for receivable account
|     pay_xref (str): external reference or alias for payable account
|     bnk1_xref (str): external reference or alias for 1st liquidity bank
|     values (dict): company data to update immediately
|     group (str): if supplied select specific group data; default is "base"
|
| Returns:
|     default company for user

setup_env
~~~~~~~~~

Create all record from declared data.

This function starts the test workflow creating the test environment.
Test data must be declared before engage this function by file .csv or
file .xlsx or by source declaration TEST_<MODEL>.

setup_env may be called more times with different group value.
If it is called with the same group, it recreates the test environment with
declared values; however this feature might do not work for some reason: i.e.
if test creates a paid invoice, the setup_env() cannot unlink invoice.
If you want to recreate the same test environment, assure the conditions for
unlink of all created and tested records.

If you create more test environment with different group you can grow the data
during test execution with complex scenario.
In this way you can create functional tests not only regression tests.

| Args:
|     lang (str): install & load specific language
|     locale (str): install locale module with CoA; i.e l10n_it
|     group (str): if supplied select specific group data; default is "base"
|     source (str): values are ("local"|"zerobug")
|     setup_list (list): list of Odoo modelS; if missed use TEST_SETUP_LIST
|     data_dir (str): data directory, default is "tests/data"
|
| Returns:
|     None

resource_edit
~~~~~~~~~~~~~

Server-side web form editing.

Ordinary Odoo test use the primitive create() and write() function to manage
test data. These methods create an update records, but they do not properly
reflect the behaviour of user editing form with GUI interface.

This function simulates the client-side form editing in the server-side.
It works in the follow way:

* It can simulate the form create record
* It can simulate the form update record
* It can simulate the user data input
* It calls the onchange functions automatically
* It may be used to call button in the form

User action simulation:

The parameter <web_changes> is a list of user actions to execute sequentially.
Every element of the list is another list with 2 or 3 values:

* Field name to assign value
* Value to assign
* Optional function to execute (i.e. specific onchange)

If field is associated to an onchange function the relative onchange functions
are execute after value assignment. If onchange set another field with another
onchange the relative another onchange are executed until all onchange are
exhausted. This behavior is the same of the form editing.

Warning: because function are always executed at the server side the behavior
may be slightly different from actual form editing. Please take note of
following limitations:

* update form cannot simulate discard button
* some required data in create must be supplied by default parameter
* form inconsistency cannot be detected by this function
* nested function must be managed by test code (i.e. wizard from form)

See test_testenv module for test examples
https://github.com/zeroincombenze/zerobug-test/tree/12.0/test_testenv

def resource_edit(self, resource, default={}, web_changes=[], actions=[], ctx={}):

    Args:
        resource (str or obj): if field is a string simulate create web behavior of
        Odoo model issued in resource;
        if field is an obj simulate write web behavior on the issued record
        default (dict): default value to assign
        web_changes (list): list of tuples (field, value); see <wiz_edit>

    Returns:
        windows action to execute or obj record

wizard
~~~~~~

Execute a full wizard.

Engage the specific wizard, simulate user actions and return the wizard result,
usually a windows action.

It is useful to test:

    * view names
    * wizard structure
    * wizard code

Both parameters <module> and <action_name> must be issued in order to
call <wiz_by_action_name>; they are alternative to act_windows.

*** Example of use ***

::

  XML view file:
      <record id="action_example" model="ir.actions.act_window">
          <field name="name">Example</field>
          <field name="res_model">wizard.example</field>
          [...]
      </record>

Python code:

::

    act_windows = self.wizard(module="module_example",
        action_name="action_example", ...)
    if self.is_action(act_windows):
        act_windows = self.wizard(act_windows=act_windows, ...)

User action simulation:

The parameter <web_changes> is a list of user actions to execute sequentially.
Every element of the list is another list with 2 or 3 values:

* Field name to assign value
* Value to assign
* Optional function to execute (i.e. specific onchange)

If field is associated to an onchange function the relative onchange functions
are execute after value assignment. If onchange set another field with another
onchange the relative another onchange are executed until all onchange are
exhausted. This behavior is the same of the form editing.

def wizard(self, module=None, action_name=None, act_windows=None, records=None, default=None, ctx={}, button_name=None, web_changes=[], button_ctx={},):

    Args:
        module (str): module name for wizard to test; if "." use current module name
        action_name (str): action name
        act_windows (dict): Odoo windows action (do not issue module & action_name)
        records (obj): objects required by the download wizard
        default (dict): default value to assign
        ctx (dict): context to pass to wizard during execution
        button_name (str): function name to execute at the end of then wizard
        web_changes (list): list of tuples (field, value); see above
        button_ctx (dict): context to pass to button_name function

    Returns:
        result of the wizard

    Raises:
        ValueError: if invalid parameters issued

validate_record
~~~~~~~~~~~~~~~

Validate records against template values.
During the test will be necessary to check result record values.
This function aim to validate all the important values with one step.
You have to issue 2 params: template with expected values and record to check.
You can declare just some field value in template which are important for you.
Both template and record are lists, record may be a record set too.
This function do following steps:

* matches templates and record, based on template supplied data
* check if all template are matched with 1 record to validate
* execute self.assertEqual() for every field in template
* check for every template record has matched with assert

def validate_records(self, template, records):

    Args:
         template (list of dict): list of dictionaries with expected values
         records (list or set): records to validate values

    Returns:
        list of matched coupled (template, record) + # of assertions

    Raises:
        ValueError: if no enough assertions or one assertion is failed

get_records_from_act_windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Get records from a windows message.

def get_records_from_act_windows(self, act_windows):

    Args:
        act_windows (dict): Odoo windows action returned by a wizard

    Returns:
        records or False

    Raises:
        ValueError: if invalid parameters issued



Getting started
===============


Prerequisites
-------------

Zeroincombenze tools requires:

* Linux Centos 7/8 or Debian 9/10 or Ubuntu 18/20/22
* python 2.7+, some tools require python 3.6+, best python 3.8+
* bash 5.0+



Installation
------------

For stable version:

`pip install z0bug_odoo`

For current version:

`cd $HOME`
`git@github.com:zeroincombenze/tools.git`
`cd $HOME/tools`
`./install_tools.sh`



Upgrade
-------

Stable version via Python Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

::

    pip install --upgrade z0bug_odoo

Current version via Git
~~~~~~~~~~~~~~~~~~~~~~~

::

    cd ./tools
    ./install_tools.sh -pUT
    source $HOME/devel/activate_tools



ChangeLog History
-----------------

2.0.19 (2024-05-11)
~~~~~~~~~~~~~~~~~~~

* [IMP] Colored test results

2.0.18 (2024-04-17)
~~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: no data loaded in some cases
* [FIX[ TestEnv: resource_edit manage read-only fields
* [QUA] TestEnv coverage 93% by test_test_env in https://github.com/zeroincombenze/zerobug-test

2.0.17 (2024-02-27)
~~~~~~~~~~~~~~~~~~~

* [IMP] TestEnv: minor improvements
* [FIX] TestEnv: crash if no account.journal in data
* [IMP] Data with date range 2024

2.0.16 (2024-02-17)
~~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: nested +multi fields with Odoo cmd

2.0.15 (2024-01-27)
~~~~~~~~~~~~~~~~~~~

* [IMP] Documentation typo corrections
* [IMP] Date range file .xlsx for TestEnv
* [IMP] TestEnv: local data dir new rules
* [FIX] TestEnv: 3 level xref, sometime fails with "_" in module name
* [FIX] TestEnv: caller environment more than 1 level
* [FIX] TestEnv: sometime is_action() fails
* [FIX] TestEnv: wizard active model
* [FIX] TestEnv: wizard module name is current module under test
* [IMP] TestEnv: binding model in view for Odoo 11.0+
* [IMP] TestEnv: write with xref can update xref id
* [IMP] TestEnv: warning if no setUp() declaration
* [IMP] TestEnv: resource_download, now default filed name is "data"


2.0.14 (2023-12-22)
~~~~~~~~~~~~~~~~~~~

* [IMP] TestEnv: commit odoo data became internal feature
* [IMP] TestEnv: test on model asset.asset
* [IMP] TestEnv: detail external reference coding free
* [IMP] TestEnv: empty currency_id is set with company currency
* [FIX] TestEnv: minor fixes in mixed environment excel + zerobug
* [FIX] TestEnv: sometimes external.KEY did not work
* [FIX] TestEnv: 3 level xref fails when module ha "_" in its name
* [IMP] _check4deps.py: documentation clearing

2.0.13 (2023-12-01)
~~~~~~~~~~~~~~~~~~~

* [IMP] TestEnv: now you can declare you own source data directory
* [IMP] TestEnv: file account.account.xlsx with l10n_generic_oca + some useful records
* [IMP] TestEnv: file account.tax.xlsx with some italian taxes for l10n_generic_oca
* [IMP] TestEnv: simple expression for data value

2.0.12 (2023-09-12)
~~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: validate_records with 2 identical template records

2.0.10 (2023-07-02)
~~~~~~~~~~~~~~~~~~~

* [IMP] TestEnv: new feature, external reference with specific field value
* [REF] TestEnv: tomany casting refactoring

2.0.9 (2023-06-24)
~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: sometimes, validate_records does not match many2one fields
* [FIX[ TestEnv: sometime crash in wizard on Odoo 11.0+ due inexistent ir.default
* [FIX] TestEnv: default value in wizard creation, overlap default function
* [FIX] TestEnv: record not found for xref of other group
* [IMP] TestEnv: resource_bind is not more available: it is replaced by resource_browse

2.0.8 (2023-04-26)
~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: multiple action on the same records

2.0.7 (2023-04-08)
~~~~~~~~~~~~~~~~~~

* [NEW] TestEnv: assertion counter
* [IMP] TestEnv: is_xref recognizes dot name, i.e "zobug.external.10"
* [IMP] TestEnv: the field <description> is not mode key (only acount.tax)
* [IMP] TestEnv: 3th level xref may be a many2one field type

2.0.6 (2023-02-20)
~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: _get_xref_id recognize any group
* [FIX] TestEnv: datetime field more precise (always with time)
* [FIX] TestEnv: resource_make / resource_write fall in crash if repeated on headr/detail models
* [NEW] TestEnv: 2many fields accepts more xref values
* [IMP] TestEnv: debug message with more icons and more readable
* [IMP] TestEnv: cast_types with formatting for python objects
* [IMP] TestEnv: validate_record now uses intelligent algorithm to match pattern templates and records

2.0.5 (2023-01-25)
~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: in some rare cases, wizard crashes
* [NEW] TestEnv: get_records_from_act_windows()
* [IMP] TestEnv: resource_make now capture demo record if available
* [IMP] TestEnv: resource is not required for declared xref
* [IMP] TestEnv: self.module has all information about current testing module
* [IMP] TestEnv: conveyance functions for all fields (currenly jsust for account.payment.line)
* [IMP] TestEnv: fields many2one accept object as value
* [IMP] TestEnv: function validate_records() improvements
* [FIX] TestEnv: company_setup, now you can declare bank account
* [IMP] TesEnv: minor improvements

2.0.4 (2023-01-13)
~~~~~~~~~~~~~~~~~~

* [FIX] TestEnv: resource_create does not duplicate record
* [FIX] TestEnv: resource_write after save calls write() exactly like Odoo behavior
* [NEW] TestEnv: new function field_download()
* [NEW] TestEnv: new function validate_records()
* [IMP] TestEnv: convert_to_write convert binary fields too
* [IMP] TestEnv: minor improvements

2.0.3 (2022-12-29)
~~~~~~~~~~~~~~~~~~

* [IMP] TestEnv: more debug messages
* [IMP] TestEnv: more improvements
* [FIX] TestEnv: sometime crashes if default use context
* [FIX] TestEnv: bug fixes

2.0.2 (2022-12-09)
~~~~~~~~~~~~~~~~~~

* [FIX] Automatic conversion of integer into string for 'char' fields
* [IMP] TestEnv

2.0.1.1 (2022-11-03)
~~~~~~~~~~~~~~~~~~~~

* [REF] clone_oca_dependencies.py

2.0.1 (2022-10-20)
~~~~~~~~~~~~~~~~~~

* [IMP] Stable version

2.0.0.1 (2022-10-15)
~~~~~~~~~~~~~~~~~~~~

* [FIX] Crash in travis

2.0.0 (2022-08-10)
~~~~~~~~~~~~~~~~~~

* [REF] Stable version



Credits
=======

Copyright
---------

SHS-AV s.r.l. <https://www.shs-av.com/>


Authors
-------

* `SHS-AV s.r.l. <https://www.zeroincombenze.it>`__



Contributors
------------

* `Antonio M. Vigliotti <info@shs-av.com>`__
* `Antonio Maria Vigliotti <antoniomaria.vigliotti@gmail.com>`__


|
|

.. |Maturity| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
    :target: https://odoo-community.org/page/development-status
    :alt: 
.. |license gpl| image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
    :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
    :alt: License: AGPL-3
.. |license opl| image:: https://img.shields.io/badge/licence-OPL-7379c3.svg
    :target: https://www.odoo.com/documentation/user/9.0/legal/licenses/licenses.html
    :alt: License: OPL
.. |Tech Doc| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-docs-2.svg
    :target: https://wiki.zeroincombenze.org/en/Odoo/2.0.19/dev
    :alt: Technical Documentation
.. |Help| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-help-2.svg
    :target: https://wiki.zeroincombenze.org/it/Odoo/2.0.19/man
    :alt: Technical Documentation
.. |Try Me| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-try-it-2.svg
    :target: https://erp2.zeroincombenze.it
    :alt: Try Me
.. |Zeroincombenze| image:: https://avatars0.githubusercontent.com/u/6972555?s=460&v=4
   :target: https://www.zeroincombenze.it/
   :alt: Zeroincombenze
.. |en| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/flags/en_US.png
   :target: https://www.facebook.com/Zeroincombenze-Software-gestionale-online-249494305219415/
.. |it| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/flags/it_IT.png
   :target: https://www.facebook.com/Zeroincombenze-Software-gestionale-online-249494305219415/
.. |check| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/check.png
.. |no_check| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/no_check.png
.. |menu| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/menu.png
.. |right_do| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/right_do.png
.. |exclamation| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/exclamation.png
.. |warning| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/warning.png
.. |same| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/same.png
.. |late| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/late.png
.. |halt| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/halt.png
.. |info| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/info.png
.. |xml_schema| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/iso/icons/xml-schema.png
   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/iso/scope/xml-schema.md
.. |DesktopTelematico| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/ade/icons/DesktopTelematico.png
   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/ade/scope/Desktoptelematico.md
.. |FatturaPA| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/ade/icons/fatturapa.png
   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/ade/scope/fatturapa.md
.. |chat_with_us| image:: https://www.shs-av.com/wp-content/chat_with_us.gif
   :target: https://t.me/Assitenza_clienti_powERP
            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/zeroincombenze/tools",
    "name": "z0bug-odoo",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "unit test debug",
    "author": "Antonio Maria Vigliotti",
    "author_email": "antoniomaria.vigliotti@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/36/7a/3581bb09a87c3c6de6959ea379cd6cc35c5580e73ece66e7acce69f23318/z0bug_odoo-2.0.19.tar.gz",
    "platform": null,
    "description": "=================\nz0bug_odoo 2.0.19\n=================\n\n\n\n|Maturity| |license gpl|\n\n\n\nOverview\n========\n\nThis package is an plug-in of **zerobug** package and aim to easily create odoo tests.\n\nIt can be used replacing OCA MQT with some nice additional features.\n\n*z0bug_odoo* is built on follow concepts:\n\n* Odoo version independent; it can test Odoo from 6.1 until 17.0\n* It is designed to run inside repository tests with `local travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_\n* It is designed to run in local environment too, using `zerobug <https://github.com/zeroincombenze/tools/tree/master/zerobug>`_\n* It can run with full or reduced set of pylint tests\n* Tests can use many ready-made database records\n* Quality Check Id\n* Keep database after tests (not inside travis and with some limitations)\n\n\ntravis ci support\n-----------------\n\nThe goal of z0bug_odoo is to provide helpers to ensure the quality of Odoo addons.\nThis package can e used replacing OCA MQT and it differs by:\n\n* z0bug_odoo can also test Odoo 6.1 and 7.0 where OCA MQT fails with these versions\n* z0bug_odoo can also test Odoo using python2 where OCA abandoned the developing of Odoo base on python2\n* z0bug_odoo is designed to execute some debug statements, mainly in local environment\n* z0bug_odoo has more options to run with reduced set of lint tests\n* OCA MQT is the only component to build environment and test Odoo while z0bug_odoo is part of `Zeroincombenze\u00ae tools <https://github.com/zeroincombenze/tools>`_\n* As per prior rule, building test environment is made by `vem <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/python_plus>`_, `clodoo <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/clodoo>`_ and `lisa <https://github.com/zeroincombenze/tools/tree/master/https://github.com/zeroincombenze/tools/tree/master/lisa>`_. These commands can also build a complete Odoo environment out of the box\n\nTo make a complete test on TravisCI your project following 3 files are required:\n\n* .travis.yml\n* requirements.txt\n* oca_dependencies.txt\n\n\nFile .travis.yml\n~~~~~~~~~~~~~~~~\n\nIn order to setup TravisCI continuous integration for your project, just copy the\ncontent of the `sample_files <https://github.com/zeroincombenze/tools/tree/master/travis_emulator/template_travis.yml>`_\nto your project\u2019s root directory.\n\nThen execute the command:\n\n::\n\n    make_travis_conf <TOOLS_PATH>/travis_emulator/template_travis.yml .travis.yml\n\nYou can check travis syntax with the `lint checker <http://lint.travis-ci.org/>`_ of travis, if available.\n\nNotice: if you do not use travisCi web site, you can avoid to set .travis.yml file.\nLocal travis emulator and z0bug_odoo create local .travis.yml dinamically.\n\n\nOdoo test integration\n~~~~~~~~~~~~~~~~~~~~~\n\nCurrent Odoo project version is declared by **VERSION** variable.\nIf your Odoo module must be tested against Odoo core,\nyou can test specific github repository by **ODOO_REPO** variable.\nYou can test against:\n\n* odoo/odoo\n* OCA/OCB\n* zeroincombenze/OCB\n\nYou can test against specific Odoo core version with ODOO_BRANCH variable if differs from your project version:\n\n::\n\n    # Odoo Branch 16.0\n    - VERSION=\"16.0\" ODOO_REPO=\"odoo/odoo\"\n\n    # Pull request odoo/odoo#143 (not in local)\n    -  VERSION=\"pull/143\" ODOO_REPO=\"OCA/OCB\"\n\n    # Branch saas-17\n    - ODOO_REPO=\"odoo/odoo\" ODOO_BRANCH=\"saas-17\"\n\n\nOCB / core test\n~~~~~~~~~~~~~~~\n\nZeroincombenze\u00ae OCB uses submodules. When test is starting, travis-ci upgrades repository and submodules.\nTo avoid submodules upgrade use this directive compatible with OCA MQT:\n\n::\n\n    - git:\n      submodules: false\n\nz0bg_odoo set security environment. You do not need to add any security statements.\nYou can avoid the following OCA MQT directive:\n\n::\n\n    - pip install urllib3[secure] --upgrade; true\n\nz0bg_odoo does some code upgrade.\nYou can avoid following directive in ODOO_TEST_SELECT=\"APPLICATIONS\":\n\n::\n\n    - sed -i \"s/self.url_open(url)/self.url_open(url, timeout=100)/g\" ${TRAVIS_BUILD_DIR}/addons/website/tests/test_crawl.py;\n\nYou can avoid following directive in ODOO_TEST_SELECT=\"LOCALIZATION\":\n\n::\n\n    - sed -i \"/'_auto_install_l10n'/d\" ${TRAVIS_BUILD_DIR}/addons/account/__manifest__.py\n\n\nPython version\n~~~~~~~~~~~~~~\n\nOdoo version from 6.1 to 10.0 are tested with python 2.7\nFrom Odoo 11.0, python3 is used. You can test against 3.6, 3.7, 3.8, 3.9 and 3.10 python versions.\nPython 3.5 still works but support is ended.\nThis is the declaration:\n\n::\n\n    python:\n      - \"3.6\"       # Odoo 11.0 12.0\n      - \"3.7\"       # Odoo 12.0\n      - \"3.8\"       # Odoo 13.0 14.0\n      - \"3.9\"       # Odoo 15.0 16.0\n      - \"3.10\"      # Odoo 17.0\n\n.. note::\n\n    python 3.5 support is ended on 2020 and 3,6 is ended on 2021.\n\n.. warning::\n\n    Currently, some Odoo version cannot support python 3.8+. See above.\n\n\nDeployment and setup environment\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn order to deploy test environment and setup code you have to declare some .travis.yml directives divides in following 3 parts:\n\n* Linux packages needed\n* PYPI packages\n* Odoo repositories dependencies\n\nLinux packages must be declared in ``<addons/apt>`` section of .travis.yml using Ubuntu namespace.\nIf you run test in local environment, travis emulator automatically translate Ubuntu names into your local distro names, if necessary.\nSee `travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_ guide for furthermore info.\n\nThe PYPI packages, installable by PIP are declared in standard PIP way, using **requirements.txt** file.\n\nIf your project depends on other Odoo Github repositories like OCA, create a file called **oca_dependencies.txt** at the root of your project and list the dependencies there.\nOne per line like so:\n\n::\n\n    project_name optional_repository_url optional_branch_name\n\nDuring testbed setup, z0bug_odoo will automatically download and place these repositories accordingly into the addon path.\nNote on addons path ordering: they will be placed after your own repo, but before the odoo core repo.\n\nIf missed optional_repository_url, the repository is searched for repository with the same owner of tested project.\n\n.. note::\n\n    This behaviour differs from OCA MQT\n\nOCA MQT always loads OCA repository while z0bug_odoo searches for current owner repository.\nSo you will test both with z0bug_odoo and both OCA MQT, always insert the full repository URL.\n\nTest execution\n~~~~~~~~~~~~~~\n\nTests run by travis_run_test command. The script is deployed in _travis directory of **zerobug** package.\nCommand have to be in ``<script>`` section of .travis.yml file:\n\n::\n\n    script:\n        - travis_run_tests\n\n\nIsolated pylint+flake8 checks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you want to make a build for these checks, you can add a line\non the ``<env>`` section of the .travis.yml file with this content:\n\n::\n\n    - VERSION=\"12.0\" LINT_CHECK=\"1\"\n\nTo avoid making again these checks on other builds, you have to add\nLINT_CHECK=\"0\" variable on the line:\n\n::\n\n    - VERSION=\"12.0\" ODOO_REPO=\"odoo/odoo\" LINT_CHECK=\"0\"\n\nYou can superset above options in local travis emulator.\n\n\nReduced set of lint check\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can execute reduced set of check, in order to gradually evolve your code quality\nwhen you meet too many errors.\n\nTo enable reduced set of check add one of follow lines:\n\n::\n\n    - LINT_CHECK=\"1\" LINT_CHECK_LEVEL=\"MINIMAL\"\n    - LINT_CHECK=\"1\" LINT_CHECK_LEVEL=\"REDUCED\"\n    - LINT_CHECK=\"1\" LINT_CHECK_LEVEL=\"AVERAGE\"\n    - LINT_CHECK=\"1\" LINT_CHECK_LEVEL=\"NEARBY\"\n    - LINT_CHECK=\"1\" LINT_CHECK_LEVEL=\"OCA\"\n\nOdoo core has internal pylint test that checks for all modules even the dependecies.\nSo if some dependecies module does not meet this test, then the full travis test fails without testing the target repository.\n\nPlease, add test_lint to EXCLUDE variable to avoid this fail-over. See below for furthermore informations.\n\nLook at follow table to understand which tests are disabled at specific level:\n\nFLAKE8 (see http://flake8.pycqa.org/en/latest/user/error-codes.html for deatils)\n\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| Test | MINIMAL    | REDUCED    | AVERAGE | NEARBY | OCA        | Note                                                                                                                             |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E117 | |no_check| | |no_check| |         |        | |no_check| | over-indented                                                                                                                    |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E121 | |no_check| | |no_check| |         |        | |no_check| | `continuation line under-indented for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E121.html>`_                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E123 | |no_check| | |no_check| |         |        | |no_check| | `Closing bracket does not match indentation of opening bracket's line <https://lintlyci.github.io/Flake8Rules/rules/E123.html>`_ |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E124 | |no_check| | |no_check| |         |        | |check|    | `Closing bracket does not match visual indentation <https://lintlyci.github.io/Flake8Rules/rules/E124.html>`_                    |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E126 | |no_check| | |no_check| |         |        | |check|    | `Continuation line over-indented for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E126.html>`_                   |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E127 | |no_check| | |no_check| |         |        | |check|    | `continuation line over-indented for visual indent <https://lintlyci.github.io/Flake8Rules/rules/E127.html>`_                    |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E128 | |no_check| | |no_check| |         |        | |check|    | `Continuation line under-indented for visual indent <https://lintlyci.github.io/Flake8Rules/rules/E128.html>`_                   |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E131 | |no_check| | |no_check| |         |        | |no_check| | `continuation line unaligned for hanging indent <https://lintlyci.github.io/Flake8Rules/rules/E131.html>`_                       |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E133 | |no_check| | |no_check| |         |        | |no_check| | `Closing bracket is missing indentation <https://lintlyci.github.io/Flake8Rules/rules/E133.html>`_                               |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E201 | |no_check| | |check|    |         |        | |check|    | `Whitespace after '(' <https://lintlyci.github.io/Flake8Rules/rules/E201.html>`_                                                 |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E202 | |no_check| | |check|    |         |        | |check|    | `Whitespace before ')' <https://lintlyci.github.io/Flake8Rules/rules/E202.html>`_                                                |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E203 | |no_check| | |check|    |         |        | |check|    | `Whitespace before ':' <https://lintlyci.github.io/Flake8Rules/rules/E203.html>`_                                                |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E211 | |no_check| | |check|    |         |        | |check|    | `whitespace before '(' <https://lintlyci.github.io/Flake8Rules/rules/E211.html>`_                                                |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E221 | |no_check| | |check|    |         |        | |check|    | `Multiple spaces before operator <https://lintlyci.github.io/Flake8Rules/rules/E221.html>`_                                      |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E222 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E225 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E226 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E231 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E241 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E242 | |no_check| | |no_check| |         |        | |no_check| |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E251 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E261 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E262 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E265 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E266 | |no_check| | |no_check| |         |        | |check|    | `too many leading '#' for block comment <https://lintlyci.github.io/Flake8Rules/rules/E266.html>`_                               |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E271 | |no_check| | |no_check| |         |        | |check|    | `multiple spaces after keyword <https://lintlyci.github.io/Flake8Rules/rules/E271.html>`_                                        |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E272 | |no_check| | |no_check| |         |        | |check|    | `multiple spaces before keyword <https://lintlyci.github.io/Flake8Rules/rules/E272.html>`_                                       |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W291 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W292 | |no_check| | |no_check| |         |        | |check|    | `no newline at end of file <https://lintlyci.github.io/Flake8Rules/rules/W292.html>`_                                            |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W293 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E301 | |no_check| | |no_check| |         |        | |check|    | `Expected 1 blank line <https://lintlyci.github.io/Flake8Rules/rules/E301.html>`_                                                |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E302 | |no_check| | |no_check| |         |        | |check|    | No __init__.py                                                                                                                   |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E303 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E305 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W391 | |no_check| | |no_check| |         |        | |check|    | blank line at end of file                                                                                                        |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| F401 | |no_check| | |check|    |         |        | |no_check| | module imported but unused                                                                                                       |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E501 | |no_check| | |no_check| |         |        | |check|    |                                                                                                                                  |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E502 | |no_check| | |no_check| |         |        | |check|    | `the backslash is redundant between brackets <https://lintlyci.github.io/Flake8Rules/rules/E502.html>`_                          |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W503 | |no_check| | |no_check| |         |        | |no_check| | No __init__.py                                                                                                                   |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| W504 | |no_check| | |no_check| |         |        | |no_check| | No __init__.py                                                                                                                   |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| F601 | |no_check| | |no_check| |         |        | |no_check| | dictionary key name repeated with different values                                                                               |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E701 | |no_check| | |no_check| |         |        | |check|    | multiple statements on one line (colon)                                                                                          |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| E722 | |no_check| | |no_check| |         |        | |check|    | do not use bare except                                                                                                           |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| F811 | |no_check| | |no_check| |         |        | |no_check| | redefinition of unused name from line N (No __init__.py)                                                                         |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n| F841 | |no_check| | |no_check| |         |        | |no_check| | `local variable 'context' is assigned to but never used <https://lintlyci.github.io/Flake8Rules/rules/F841.html>`_               |\n+------+------------+------------+---------+--------+------------+----------------------------------------------------------------------------------------------------------------------------------+\n\n\n\n\nPYLINT (see http://pylint-messages.wikidot.com/all-codes for details)\n\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| Test  | MINIMAL    | REDUCED    | AVERAGE | NEARBY | OCA     | Notes                                                                               |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W0101 | |no_check| | |no_check| |         |        | |check| | `unreachable <http://pylint-messages.wikidot.com/messages:w0101>`_                  |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W0312 | |no_check| | |check|    |         |        | |check| | `wrong-tabs-instead-of-spaces <http://pylint-messages.wikidot.com/messages:w0312>`_ |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W0403 | |no_check| | |no_check| |         |        | |check| | relative-import                                                                     |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W1401 | |no_check| | |check|    |         |        | |check| | anomalous-backslash-in-string                                                       |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| E7901 | |no_check| | |no_check| |         |        | |check| | `rst-syntax-error <https://pypi.org/project/pylint-odoo/1.4.0>`_                    |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| C7902 | |no_check| | |check|    |         |        | |check| | missing-readme                                                                      |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7903 | |no_check| | |no_check| |         |        | |check| | javascript-lint                                                                     |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7908 | |no_check| | |no_check| |         |        | |check| | missing-newline-extrafiles                                                          |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7909 | |no_check| | |no_check| |         |        | |check| | redundant-modulename-xml                                                            |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7910 | |no_check| | |check|    |         |        | |check| | wrong-tabs-instead-of-spaces                                                        |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7930 | |no_check| | |no_check| |         |        | |check| | `file-not-used <https://pypi.org/project/pylint-odoo/1.4.0>`_                       |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7935 | |no_check| | |no_check| |         |        | |check| | missing-import-error                                                                |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7940 | |no_check| | |no_check| |         |        | |check| | dangerous-view-replace-wo-priority                                                  |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W7950 | |no_check| | |no_check| |         |        | |check| | odoo-addons-relative-import                                                         |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| E8102 | |no_check| | |check|    |         |        | |check| | invalid-commit                                                                      |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| C8103 | |no_check| | |check|    |         |        | |check| | `manifest-deprecated-key <https://pypi.org/project/pylint-odoo/1.4.0>`_             |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W8103 | |no_check| | |no_check| |         |        | |check| | translation-field                                                                   |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| C8104 | |no_check| | |no_check| |         |        | |check| | `class-camelcase <https://pypi.org/project/pylint-odoo/1.4.0>`_                     |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W8104 | |no_check| | |no_check| |         |        | |check| | api-one-deprecated                                                                  |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| C8105 | |no_check| | |check|    |         |        | |check| | `license-allowed <https://pypi.org/project/pylint-odoo/1.4.0>`_                     |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| C8108 | |no_check| | |no_check| |         |        | |check| | method-compute                                                                      |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| R8110 | |no_check| | |check|    |         |        | |check| | old-api7-method-defined                                                             |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| W8202 | |no_check| | |check|    |         |        | |check| | use-vim-comment                                                                     |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |check|    |         |        | |check| | sql-injection                                                                       |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |check|    |         |        | |check| | duplicate-id-csv                                                                    |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |no_check| |         |        | |check| | create-user-wo-reset-password                                                       |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |no_check| |         |        | |check| | dangerous-view-replace-wo-priority                                                  |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |no_check| |         |        | |check| | translation-required                                                                |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |check|    |         |        | |check| | duplicate-xml-record-id                                                             |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |no_check| |         |        | |check| | no-utf8-coding-comment                                                              |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |check|    |         |        | |check| | attribute-deprecated                                                                |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n| N/A   | |no_check| | |no_check| |         |        | |check| | consider-merging-classes-inherited                                                  |\n+-------+------------+------------+---------+--------+---------+-------------------------------------------------------------------------------------+\n\n\n\n\nDisable some pylint and/or flake8 checks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can disable some specific test or some file from lint checks.\n\nTo disable flake8 checks on specific file you can add following line at the beginning of python file:\n\n::\n\n    # flake8: noqa\n\nTo disable pylint checks on specific file you can add following line at the beginning of python file:\n\n::\n\n    # pylint: skip-file\n\nTo disable both flake8 and pylint checks on specific file you can add following line at the beginning of python file:\n\n::\n\n    # flake8: noqa - pylint: skip-file\n\nTo disable pylint checks on specific XML file you can add following line in XML file after xml declaration:\n\n::\n\n    <!-- pylint:disable=deprecated-data-xml-node -->\n\nYou can disable specific flake8 check in some source part of python file adding a comment at the same statement to disable check. Here an example to disable sql error (notice comment must be at beginning of the statement):\n\n::\n\n    from builtins import *  # noqa: F403\n\nIf you have to disable more than one error you can add following declaration:\n\n::\n\n    from builtins import *  # noqa\n\nYou can also disable specific pylint check in some source part of python file adding a comment at the same statement to disable check. Here an example to disable sql error (notice comment must be at beginning of the statement):\n\n::\n\n    self._cr.execute()      # pylint: disable=E8103\n\n\nDisable unit test\n~~~~~~~~~~~~~~~~~\n\nIf you want to make a build without tests, you can use the following directive:\n``TEST_ENABLE=\"0\"``\n\nYou will simply get the databases with packages installed,\nbut without running any tests.\n\n\nReduced set of unit test\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nOdoo modules may fail in Travis CI or in local environment.\nCurrently Odoo OCB core tests fail; we are investigating for the causes.\nHowever you can use a simple workaround, disabling some test.\nCurrently tests fail are:\n\n* test_impex\n* test_ir_actions\n* test_lint\n* test_main_flows\n* test_search\n* test_user_has_group\n\nExample:\n\n::\n\n    - export EXCLUDE=test_impex,test_ir_actions,test_lint,test_main_flows,test_search,test_user_has_group\n    - TESTS=\"1\" ODOO_TEST_SELECT=\"ALL\"\n    - TESTS=\"1\" ODOO_TEST_SELECT=\"NO-CORE\"\n    - ....\n\nYou can set parameter local GBL_EXCLUDE to disable these test for all repositories.\nYou will be warned that local GBL_EXCLUDE has only effect for local emulation.\nTo avoid these test on web travis-ci you have to set EXCLUDE value in .travis.yml file.\n\nLook at follow table to understand which set of tests are enabled or disabled:\n\n+--------------------+--------------+--------------+--------------+-------------------------+\n| statement          | application  | local module | odoo/addons  | addons + dependencies   |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| ALL                | |check|      | |check|      | |check|      | |check|                 |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| APPLICATIONS       | |check|      | |no_check|   | |no_check|   | Only if application     |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| LOCALIZATION       | |no_check|   | |check|      | |no_check|   | Only local modules      |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| CORE               | |no_check|   | |no_check|   | |check|      | |no_check|              |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| NO-APPLICATION     | |no_check|   | |check|      | |check|      | No if application       |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| NO-LOCALIZATION    | |check|      | |no_check|   | |check|      | No local modules        |\n+--------------------+--------------+--------------+--------------+-------------------------+\n| NO-CORE            | |check|      | |check|      | |no_check|   | |check|                 |\n+--------------------+--------------+--------------+--------------+-------------------------+\n\n\n\n\nDependencies test\n~~~~~~~~~~~~~~~~~\n\nSince late Summer 2021, z0bug_odoo checks for dependencies.\nThis test is a sub test of unit test. This is the directive:\n\n::\n\n    - TESTS=\"1\" TEST_DEPENDENCIES=\"1\"\n\n\nModule unit tests\n~~~~~~~~~~~~~~~~~\n\nz0bug_odoo is also capable to test each module individually.\nThe intention is to check if all dependencies are correctly defined.\nActivate it through the ``UNIT_TEST`` directive.\nAn additional line should be added to the ``env:`` section,\nsimilar to this one:\n\n::\n\n    - VERSION=\"12.0\" UNIT_TEST=\"1\"\n\n\nAutomatic module translation\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSince late Summer 2021, z0bug_odoo activate automatic module translation after test ended with success.\nThis is the directive:\n\n::\n\n    - VERSION=\"12.0\" ODOO_TNLBOT=\"1\"\n\nThis feature is still experimental.\n\n\nNames used for the test databases\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nz0bug_odoo has a nice feature of organizing your testing databases.\nYou might want to do that if you want to double them up as\nstaging DBs or if you want to work with an advanced set of\ntemplates in order to speed up your CI pipeline.\nJust specify at will:\n\n``MQT_TEMPLATE_DB='odoo_template' MQT_TEST_DB='odoo_test'``\n\nIn your local travis you can declare the default value but these values are not applied in web TravisCi web site.\n\nDatabase user is the current username. This behavior works both in local test both in TravisCi web site.\nHowever, sometimes, local user and db username can be different. You can set the default value in travis emulator.\n\n\nCoveralls/Codecov configuration file\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`Coveralls <https://coveralls.io/>`_ and `Codecov <https://codecov.io/>`_ services provide information on the test coverage of your modules.\nCurrently both configurations are automatic (check default configuration `here <cfg/.coveragerc>`_.\nSo, as of today, you don't need to include a ``.coveragerc`` into the repository,\nIf you do it, it will be simply ignored.\n\n\nOther configurations\n~~~~~~~~~~~~~~~~~~~~\n\nYou can highly customize you test: look at below table.\n\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| variable               | default value                                          | meaning                                                                   |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| CHROME_TEST            |                                                        | Set value to 1 to use chrome client to test                               |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| DATA_DIR               | ~/data_dir                                             | Odoo data directory (data_dir in config file)                             |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| EXCLUDE                |                                                        | Modules to exclude from test                                              |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| INCLUDE                |                                                        | Modules to test (all                                                      |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| INSTALL_OPTIONS        |                                                        | Options passed to odoo-bin/openerp-server to install modules              |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| MQT_DBSUER             | $USER                                                  | Database username                                                         |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| MQT_TEMPLATE_DB        | template_odoo                                          | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| MQT_TEST_DB            | test_odoo                                              | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| NPM_CONFIG_PREFIX      | \\$HOME/.npm-global                                     | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| ODOO_COMMIT_TEST       | 0                                                      | Test result will be committed; require specific code at TearDown function |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| ODOO_REPO              | odoo/odoo                                              | OCB repository against test repository                                    |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| ODOO_SETUPS            | __manifest__.py __openerp__.py __odoo__.py __terp__.py | Names of Odoo manifest files                                              |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| ODOO_TEST_SELECT       | ALL                                                    | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| ODOO_TNLBOT            | 0                                                      | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| OPTIONS                |                                                        | Options passed to odoo-bin/openerp-server to execute tests                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PHANTOMJS_VERSION      |                                                        | Version of PhantomJS                                                      |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_TXT_COLOR           | 0;97;40                                                | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_RUN_COLOR           | 1;37;44                                                | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_NOP_COLOR           | 34;107                                                 | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_HDR1_COLOR          | 97;42                                                  | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_HDR2_COLOR          | 30;43                                                  | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PS_HDR3_COLOR          | 30;45                                                  | N/D                                                                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| PYPI_RUN_PYVER         | (2.7|3.5|3.6|3.7|3.8|3.9)                              | python versions to run (only PYPI projects)                               |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| SERVER_EXPECTED_ERRORS |                                                        | # of expected errors after tests                                          |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| TEST_DEPENDENCIES      | 0                                                      | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| TRAVIS_DEBUG_MODE      | 0                                                      | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| TRAVIS_PDB             |                                                        | The value 'true' activates pdb in local 'travis -B'                       |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| UNBUFFER               | 1                                                      | Use unbuffer (colors) to log results                                      |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| UNIT_TEST              |                                                        | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| TEST                   |                                                        | Read above                                                                |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| VERSION                |                                                        | Odoo version to test (see above)                                          |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| WEBSITE_REPO           |                                                        | Load package for website tests                                            |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n| WKHTMLTOPDF_VERSION    | 0.12.5                                                 | Version of wkhtmltopdf (value are 0.12.1                                  |\n+------------------------+--------------------------------------------------------+---------------------------------------------------------------------------+\n\n\n\n\n\nDebug information\n~~~~~~~~~~~~~~~~~\n\nIf you declare the following directive in <env global> section:\n\n``TRAVIS_DEBUG_MODE=\"n\"``\n\nwhere \"n\" means:\n\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| Parameter                 | 0           | 1           | 2           | 3        | 9            |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| Informative messages      | |no_check|  | |check|     | |check|     | |check|  | |check|      |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| Inspect internal data     | |no_check|  | |no_check|  | |check|     | |check|  | |check|      |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| MQT tests                 | |no_check|  | |no_check|  | |no_check|  | |check|  | |check|      |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| Installation log level    | ERROR       | WARN        | INFO        | INFO     | |no_check|   |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n| Execution log level       | INFO        | TEST        | TEST        | TEST     | |no_check|   |\n+---------------------------+-------------+-------------+-------------+----------+--------------+\n\n\n\nNote this feature does not work with OCA MQT. Local test and TravisCI test have slightly different behavior.\n\nWhen MQT is execute in local environment the value\n\n``TRAVIS_DEBUG_MODE=\"9\"``\n\ndoes not execute unit test. It is used to debug MQT itself.\n\nSee `local travis emulator <https://github.com/zeroincombenze/tools/tree/master/travis_emulator>`_\n\n\nTree directory\n~~~~~~~~~~~~~~\n\nWhile travis is running this is the tree directory:\n\n::\n\n    ${HOME}                         # home of virtual environment (by TravisCI)\n    \u2523\u2501\u2501 build                       # build root (by TravisCI)\n    \u2503    \u2523\u2501\u2501 ${TRAVIS_BUILD_DIR}    # testing repository (by TravisCI)\n    \u2503    \u2517\u2501\u2501 ${ODOO_REPO}           # odoo or OCB repository to check with       (0) (1) (2)\n    \u2503\n    \u2523\u2501\u2501 ${ODOO_REPO}-${VERSION}     # symlink of ${HOME}/build/{ODOO_REPO}       (0) (1)\n    \u2503\n    \u2523\u2501\u2501 dependencies                # Odoo dependencies of repository            (0) (3)\n    \u2503\n    \u2523\u2501\u2501 tools                       # clone of Zeroincombenze tools              (3) (4)\n    \u2503    \u2503\n    \u2503    \u2523\u2501\u2501 zerobug                # z0bug testing library\n    \u2503    \u2503       \u2517\u2501\u2501 _travis        # testing commands\n    \u2503    \u2517\u2501\u2501 z0bug_odoo             # Odoo testing library\n    \u2503            \u2517\u2501\u2501 travis         # testing commands\n    \u2503\n    \u2517\u2501\u2501 maintainer-quality-tools    # OCA testing library\n         \u2517\u2501\u2501 travis                 # testing commands\n\n    (0) Same behavior of OCA MQT\n    (1) Cloned odoo/odoo or OCA/OCB repository to check compatibility of testing modules\n    (2) If the testing project is OCB, travis_install_env ignore this directory\n    (3) Done by then following statements in .travis.yml:\n        - travis_install_env\n        Above statements replace the OCA statements:\n        - travis_install_nightly\n    (4) Done by following statements in .travis.yml::\n        - git clone https://github.com/zeroincombenze/tools.git ${HOME}/tools --depth=1\n        - \\${HOME}/tools/install_tools.sh -qpt\n        - source ${HOME}/devel/activate_tools -t\n        Above statements replace OCA following statements:\n        - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools --depth=1\n        - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}\n\nTestEnv: the test environment\n-----------------------------\n\nTestEnv makes available a test environment ready to use in order to test your Odoo\nmodule in quick and easy way.\n\nThe purpose of this software are:\n\n* Create the Odoo test environment with records to use for your test\n* Make available some useful functions to test your module\n* Simulate the wizard to test wizard functions (wizard simulator)\n* Environment running different Odoo modules versions\n* Keep database after tests (with some limitations)\n\nPlease, pay attention to test data: TestEnv use internal unicode even for python 2\nbased Odoo (i.e. 10.0). You should declare unicode date whenever is possible.\n\n.. note::\n\n    Odoo core uses unicode even on old Odoo version.\n\nFor a complete set of examples, please look at the module test_testenv in\n`repository <https://github.com/zeroincombenze/zerobug-test>`__\n\nTests are based on test environment created by module mk_test_env in\n`repository <https://github.com/zeroincombenze/zerobug-test>`__\n\nkeeping database after tests\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nUsing zerobug in conjunction with z0bug_odoo a nice feature is available: you can keep\nthe database after tests, so you can touch teh results or build example DB.\nHowever this feature has some limitation:\n\n    #. You can use just 1 test class, because saving is made on TearDown execution\n    #. You cannot create on fly record with external reference of current module name\n\nExample 1, double test class: it does not work\n\n::\n\n    class TestExample(SingleTransactionCase):\n        ...\n\n    class Test2Example(SingleTransactionCase):\n        ...\n\nExample 2, module named \"my_module\":\n\n::\n\n    class TestExample(SingleTransactionCase):\n        ...\n        # Follow record with external reference \"my_module.my_xref\" will be\n        # automaticaaly deleted by Odoo at the end of the test\n        self.resource_create(\"my.model\", xref=\"my_module.my_xref\", ...\n        # Follow record with external reference \"z0bug.my_xref\" works!\n        self.resource_create(\"my.model\", xref=\"z0bug.my_xref\", ...\n\nNames used for the test databases in testenv\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nzerobug and z0bug_odoo use different rule from travis emulatro to naming test database.\nThe database name is \"test_<MODULE_NAME>\".\n\nPlease, notice a template database, named \"template_<MODULE_NAME>\" is built before test\ndatabase and then ie kept in the system.\nIf you do not want to see template databases use following regex for dbfilter parameter\ninf your Odoo configuration file:\n\n    dbfilter = (?!template).*\n\nRequirements\n~~~~~~~~~~~~\n\nThs TestEnv software requires:\n\n* python_plus PYPI package\n* z0bug_odoo PYPI package 2.0.19\n* python 2.7 / 3.6 / 3.7 / 3.8\n\nTestEnv is full integrated with Zeroincombenze\u00ae tools.\nSee `readthedocs <https://zeroincombenze-tools.readthedocs.io/>`__\nand `zeroincombenze github <https://github.com/zeroincombenze/tools.git>`__\nZeroincombenze\u00ae tools help you to test Odoo module with pycharm.\n\n\n\nFeatures\n--------\n\nData to use in tests are store in csv files in data directory.\nFile names are tha name of the models (table) with characters '.' (dot) replaced by '_' (underscore)\n\nHeader of file must be the names of table fields.\n\nRows can contains value to store or Odoo external reference or macro.\n\nFor type char, text, html, int, float, monetary: value are constants inserted as is.\n\nFor type many2one: value may be an integer (record id) or Odoo external reference (format \"module.name\").\n\nFor type data, datetime: value may be a constant or relative date\n\n\n\nUsage\n=====\n\nUsage Details\n-------------\n\nYou can locate the recent testenv.py in testenv directory of module\n`z0bug_odoo <https://github.com/zeroincombenze/tools/tree/master/z0bug_odoo/testenv>`__\n\nFor full documentation visit:\n`zero-tools <https://zeroincombenze-tools.readthedocs.io/en/latest/pypi_z0bug_odoo/index.html>`__\nor\n`z0bug_odoo <https://z0bug-odoo.readthedocs.io/en/latest/>`__\nor\n`zero-tools (github) <https://github.com/zeroincombenze/tools>`__\nor\n`github with example modules <https://github.com/zeroincombenze/zerobug-test>`__\n\nCopy the testenv.py file in tests directory of your module.\nPlease copy the documentation testenv.rst file in your module too.\n\nThe __init__.py must import testenv.\n\n::\n\n    from . import testenv\n    from . import test_<MY_TEST_FILE>\n\nYour python test file have to contain some following example lines:\n\n::\n\n    import os\n    import logging\n    from .testenv import MainTest as SingleTransactionCase\n\n    _logger = logging.getLogger(__name__)\n\n    TEST_SETUP_LIST = [\"res.partner\", ]\n\n    class MyTest(SingleTransactionCase):\n\n        def setUp(self):\n            super().setUp()\n            # Add following statement just for get debug information\n            self.debug_level = 2\n            # keep data after tests\n            self.odoo_commit_data = True\n            self.setup_env()                # Create test environment\n\n        def tearDown(self):\n            super().tearDown()\n\n        def test_mytest(self):\n            _logger.info(\n                \"\ud83c\udfba Testing test_mytest\"    # Use unicode char to best log reading\n            )\n            ...\n\nAn important helper to debug is self.debug_level. When you begins your test cycle,\nyou are hinted to set self.debug_level = 3; then you can decrease the debug level\nwhen you are developing stable tests.\nFinal code should have self.debug_level = 0.\nTestEnv logs debug message with symbol \"\ud83d\udc1e \" so you can easily recognize them.\nAnother useful helper is the database keep data after test feature. You have to declare\nself.odoo_commit_data = True and you have to set global bash environment\n\n``global ODOO_COMMIT_DATA=\"1\"``\n\nThs TestEnv software requires:\n\n* python_plus PYPI package\n* z0bug_odoo PYPI package version 2.0.19\n* python 2.7 / 3.6 / 3.7 / 3.8 / 3.9 / 3.10\n\n\n\nModel data declaration\n~~~~~~~~~~~~~~~~~~~~~~\n\nEach model is declared in a csv file or xlsx file in **test/data** directory of the\nmodule. The file name is the same of model name with dots replaced by undescore.\n\ni.e. below the contents of **res_parter.csv** file:\n\n::\n\n    id,name,street\n    z0bug.partner1,Alpha,\"1, First Avenue\"\n\nThe model may also be declared in a dictionary which key which is the external\nreference used to retrieve the record.\n\ni.e. the following record declaration is the same of above example; record id is named\n``z0bug.partner1`` in res.partner:\n\n::\n\n    TEST_RES_PARTNER = {\n        \"z0bug.partner1\": {\n            \"name\": \"Alpha\",\n            \"street\": \"1, First Avenue\",\n            ...\n        }\n    )\n\n.. warning::\n\n    Please, do not to declare ``product.product`` records: they are automatically\n    created as child of ``product.template``. The external reference must contain\n    the pattern ``_template`` (see below).\n\n.. warning::\n\n    When you write a file with a spreadsheet app, pay attention to automatic string\n    replacement. For example double quote char <\"> may be replaced by <\u201d>.\n    These replaced characters may be create some troubles during import data step,\n    expecially when used in \"python expression\".\n\n\n\nMagic relationship\n~~~~~~~~~~~~~~~~~~\n\nSome models/tables should be managed together, i.e. **account.move** and **account.move.line**.\nTestEnv manages these models/tables, called header/detail, just as a single object.\nWhen header record is created, all detail lines are created with header.\nOdoo standard declaration requires the details data in child reference field with\ncommand *0, 0*.\nThis method make unreadable the source data. Look at the simple follow example with\nusually Odoo declaration way:\n\n::\n\n    sale_order_data = {\n        \"example.order_1\": {\n            \"partner_id\": self.env.ref(\"base.res_partner_1\"),\n            \"origin\": \"example\",\n            ...\n            \"order_line\": [\n                (0, 0, {\n                    \"product_id\": self.env.ref(\"product.product_product_1\"),\n                    \"product_qty\": 1,\n                    \"price_unit\": 1.23,}),\n                (0, 0, {\n                    \"product_id\": self.env.ref(\"product.product_product_2\"),\n                    \"product_qty\": 2,\n                    \"price_unit\": 2.34,}),\n            ]\n        }\n\n    }\n\nNow look at the same data in internal declaration by **z0bug_odoo**:\n\n::\n\n    TEST_SALE_ORDER = {\n        \"example.order_1\": {\n            \"partner_id\": \"base.res_partner_1\",\n            \"origin\": \"example\",\n            ...\n        }\n\n    }\n\n    TEST_SALE_ORDER_LINE = {\n        \"example.order_1_1\": {\n            \"product_id\": \"product.product_product_1\",\n            \"product_qty\": 1,\n            \"price_unit\": 1.23,\n        },\n        \"example.order_1_2\": {\n            \"product_id\": \"product.product_product_2\",\n            \"product_qty\": 2,\n            \"price_unit\": 2.34,\n        }\n    }\n\nAs you can see, the data is easy readable and easy updatable. Please, notice:\n\n#. Sale order lines are declared in specific model **sale.order.line**\n#. Record ID **must** begin with header ID, followed by \"_\" and line ID\n#. Reference data do not require ``self.env.ref()``: they are automatically referenced\n\nIt is also easy write the csv or xlsx file. This is the example with above data\n\nFile **sale_order.csv**\n\n::\n\n    id,partner_id,origin\n    example.order_1,base.res_partner_1,example\n\nFile **sale_order_line.csv**\n\n::\n\n    id,product_id,product_qty,price_unit\n    example.order_1_1,product.product_product_1,1,1.23\n    example.order_1_2,product.product_product_2,2,2.34\n\nIn your test file you must declare the following statement:\n\n::\n\n    TEST_SETUP_LIST = [\"sale.order\", \"sale.order.line\"]\n\n.. warning::\n\n    You must declare header and lines data before create header record\n\n.. note::\n\n    External reference coding is free: however is hinted to use the The 2\n    keys reference explained in \"External reference\" chapter.\n\nAnother magic relationship is the **product.template** (product) / **product.product** (variant)\nrelationship.\nWhenever a **product.template** (product) record is created,\nOdoo automatically creates one variant (child) record for **product.product**.\nIf your test module does not need to manage product variants you can avoid to declare\n**product.product** data even if this model is used in your test data.\n\nFor example, you have to test **sale.order.line** which refers to **product.product**.\nYou simply declare a **product.template** record with external reference\nuses \"_template\" magic text.\n\n::\n\n    TEST_PRODUCT_TEMPLATE = {\n        \"z0bug.product_template_1\": {\n            \"name\": \"Product alpha\",\n            ...\n        }\n    )\n\n    ...\n\n    TEST_SALE_ORDER_LINE = {\n        \"z0bug.order_1_1\": {\n            \"product_id\": \"z0bug.product_product_1\",\n            ...\n        }\n    )\n\n\n\nExternal reference\n~~~~~~~~~~~~~~~~~~\n\nEvery record tagged by an external reference may be:\n\n    * Ordinary Odoo external reference ``(a)``, format \"module.name\"\n    * Test reference, format \"z0bug.name\" ``(b)``\n    * Key value, format \"external.key\" ``(c)``\n    * 2 keys reference, for header/detail relationship ``(d)``\n    * Magic reference for **product.template** / **product.product** ``(e)``\n\nOrdinary Odoo external reference ``(a)`` is a record of **ir.model.data**;\nyou can see them from Odoo GUI interface.\n\nTest reference ``(b)`` are visible just in the test environment.\nThey are identified by \"z0bug.\" prefix module name.\n\nExternal key reference ``(c)`` is identified by \"external.\" prefix followed by\nthe key value used to retrieve the record.\nIf key value is an integer it is the record \"id\".\nThe field \"code\" or \"name\" are used to search record;\nfor account.tax the \"description\" field is used.\nPlease set self.debug_level = 2 (or more) to log these field keys.\n\nThe 2 keys reference ``(d)`` needs to address child record inside header record\nat 2 level model (header/detail) relationship.\nThe key MUST BE the same key of the parent record,\nplus \"_\", plus line identifier (usually **sequence** field).\ni.e. ``z0bug.move_1_3`` means: line with sequence ``3`` of **account.move.line**\nwhich is child of record ``z0bug.move_1`` of **account.move**.\nPlease set self.debug_level = 2 (or more) to log these relationships.\n\nFor **product.template** (product) you must use '_template' text in reference ``(e)``.\nTestEnv inherit **product.product** (variant) external reference\n(read above \"Magic relationship\").\n\nExamples:\n\n::\n\n    TEST_ACCOUNT_ACCOUNT = {\n        \"z0bug.customer_account\": {\n            \"code\": \"\", ...\n        }\n        \"z0bug.supplier_account\": {\n            \"code\": \"111100\", ...\n        }\n    )\n\n    ...\n\n    self.resource_edit(\n        partner,\n        web_changes = [\n            (\"country_id\", \"base.it\"),       # Odoo external reference (type a)\n            (\"property_account_receivable_id\",\n             \"z0bug.customer_account\"),      # Test reference (type b)\n            (\"property_account_payable_id\",\n             \"external.111100\"),             # External key (type c)\n        ],\n    )\n\n\n\nModule test execution session\n-----------------------------\n\nIntroduction\n~~~~~~~~~~~~\n\nModule test execution workflow should be:\n\n    #. Data declaration, in file .csv or .xlsz\u00ecx or in source code\n    #. Base data creation, in setUp() function\n    #. Tests execution\n    #. Supplemental data creation, during test execution, by group name\n\nTest data may be managed by one or more data group; if not declared,\n\"base\" group name is used. The \"base\" group will be created at the setUp()\nlevel: it is the base test data.\nTesting function may declare and manage other group data. Look at the\nfollowing example:\n\n::\n\n    import os\n    import logging\n    from .testenv import MainTest as SingleTransactionCase\n\n    _logger = logging.getLogger(__name__)\n\n    TEST_PRODUCT_TEMPLATE = {\n        \"z0bug.product_template_1\": {...}\n    }\n    TEST_RES_PARTNER = {\n        \"z0bug.partner1\": {...}\n    )\n    TEST_SETUP_LIST = [\"res.partner\", \"product.template\"]\n\n    TEST_SALE_ORDER = {\n        \"z0bug.order_1\": {\n            \"partner_id\": \"z0bug.partner1\",\n            ...\n        }\n    }\n    TEST_SALE_ORDER_LINE = {\n        \"z0bug.order_1_1\": {\n            \"product_id\": \"z0bug.product_product_1\",\n            ...\n        }\n    )\n\n    class MyTest(SingleTransactionCase):\n\n        def setUp(self):\n            super().setUp()\n            self.debug_level = 2\n            self.setup_env()                # Create base test environment\n\n        def test_something(self):\n            # Now add Sale Order data, group \"order\"\n            self.setup_env(group=\"order\", setup_list=[\"sale.order\", \"sale.order.line\"])\n\nNote the external reference are globals and they are visible from any groups.\nAfter base data is created, the real test session can begin. You can simulate\nvarious situation; the most common are:\n\n    #. Simulate web form create record\n    #. Simulate web form update record\n    #. Simulate the multi-record windows action\n    #. Download any binary data created by test\n    #. Engage wizard\n\n.. note::\n\n    You can also create / update record with usually create() / write() Odoo function,\n    but they do not really simulate the user behavior because they do not engage the\n    onchange methods, they do not load any view and so on.\n\nThe real best way to test a create record is like the follow example\nbased on **res.partner model**:\n\n::\n\n        partner = self.resource_edit(\n            resource=\"res.partner\",\n            web_changes=[\n                (\"name\", \"Adam\"),\n                (\"country_id\", \"base.us\"),\n                ...\n            ],\n        )\n\nYou can also simulate the update session, issuing the record:\n\n::\n\n        partner = self.resource_edit(\n            resource=partner,\n            web_changes=[\n                (\"name\", \"Adam Prime\"),\n                ...\n            ],\n        )\n\nLook at resource_edit() documentation for furthermore details.\n\nIn you test session you should need to test a wizard. This test is very easy\nto execute as in the follow example that engage the standard language install\nwizard:\n\n::\n\n        # We engage language translation wizard with \"it_IT\" language\n        # see \"<ODOO_PATH>/addons/base/module/wizard/base_language_install*\"\n        _logger.info(\"\ud83c\udfba Testing wizard.lang_install()\")\n        act_windows = self.wizard(\n            module=\"base\",\n            action_name=\"action_view_base_language_install\",\n            default={\n                \"lang\": \"it_IT\"\n                \"overwrite\": False,\n            },\n            button_name=\"lang_install\",\n        )\n        self.assertTrue(\n            self.is_action(act_windows),\n            \"No action returned by language install\"\n        )\n        # Now we test the close message\n        self.wizard(\n            act_windows=act_windows\n        )\n        self.assertTrue(\n            self.env[\"res.lang\"].search([(\"code\", \"=\", \"it_IT\")]),\n            \"No language %s loaded!\" % \"it_IT\"\n        )\n\nLook at wizard() documentation for furthermore details.\n\n\n\nData values\n-----------\n\nData values may be raw data (string, number, dates, etc.) or external reference\nor some macro.\nYou can declare data value on your own but you can discover the full test environment\nin https://github.com/zeroincombenze/zerobug-test/mk_test_env/ and get data\nfrom this environment.\n\n.. note::\n\n    The fields **company_id** and **currency_id** may be empty to use default value.\n    If you want to issue no value, do not declare column in model file (csv or xlsx).\n\nYou can evaluate the field value engaging a simple python expression inside tags like in\nfollowing syntax:\n\n    \"<?odoo EXPRESSION ?>\"\n\nThe expression may be a simple python expression with following functions:\n\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| function     | description                                   | example                                         |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| compute_date | Compute date                                  | <?odoo compute_date('<###-##-##')[0:4] ?>       |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| random       | Generate random number from 0.0 to 1.0        | <?odoo int(random() * 1000) ?>                  |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| ref          | Odoo reference self.env.ref()                 | <?odoo ref('product.product_product_1') ?>      |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| ref[field]   | field of record of external reference         | <?odoo ref('product.product_product_1').name ?> |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n| ref[field]   | field of record of external reference (brief) | <?odoo product.product_product_1.name ?>        |\n+--------------+-----------------------------------------------+-------------------------------------------------+\n\n\n\ncompany_id\n~~~~~~~~~~\n\nIf value is empty, user company is used.\nThis behavior is not applied on\n**res.users**, **res.partner**, **product.template** and **product.product** models.\nFor these models you must fill the **company_id** field.\n\nWhen data is searched by ``resource_search()`` function on every model with company_id,\nthe **company_id** field is automatically added to search domain, using 'or' between\ncompany_id null and company_id equal to supplied value or current user company.\n\n\n\nboolean\n~~~~~~~\n\nYou can declare boolean value:\n\n* by python boolean False or True\n* by integer 0 or 1\n* by string \"0\" or \"False\" or \"1\" or \"True\"\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"supplier\": False,\n                \"customer\": \"True\",\n                \"is_company\": 1,\n            }\n        }\n    )\n\n\n\nchar / text\n~~~~~~~~~~~\n\nChar and Text values are python string; please use unicode whenever is possible\neven when you test Odoo 10.0 or less.\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                \"name\": \"Alpha\",\n                \"street\": \"1, First Avenue\"\n                # Name of Caserta city\n                \"city\": \"<? base.state_it_ce.name ?>\",\n                # Reference: 'year/123'\n                \"ref\": \"<? compute_date('####-##-##')[0:4] + '/123' ?>\",\n            }\n        }\n    )\n\n\n\ninteger / float / monetary\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nInteger, Floating and Monetary values are python integer or float.\nIf numeric value is issued as string, it is internally converted\nas integer/float.\n\n::\n\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"color\": 1,\n                \"credit_limit\": 500.0,\n                \"payment_token_count\": \"0\",\n            }\n        }\n    )\n\n\n\ndate / datetime\n~~~~~~~~~~~~~~~\n\nDate and Datetime value are managed in special way.\nThey are processed by ``compute_date()`` function (read below).\nYou can issue a single value or a 2 values list, 1st is the date,\n2nd is the reference date.\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"activity_date_deadline\": \"####-1>-##\",    # Next month\n                \"signup_expiration\": \"###>-##-##\",         # Next year\n                \"date\": -1,                                # Yesterday\n                \"last_time_entries_checked\":\n                    [+2, another_date],                    # 2 days after another day\n                \"message_last_post\": \"2023-06-26\",         # Specific date, ISO format\n            }\n        }\n    )\n\n\n\nmany2one\n~~~~~~~~\n\nYou can issue an integer (if you know exactly the ID)\nor an external reference. Read above about external reference.\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"country_id\": \"base.it\",                   # Odoo external reference\n                \"property_account_payable_id\":\n                    \"z0bug.customer_account\",              # Test record\n                \"title\": \"external.Mister\"                 # Record with name==\"Mister\"\n            }\n        }\n    )\n\n\n\none2many / many2many\n~~~~~~~~~~~~~~~~~~~~\n\nThe one2many and many2many field may contains one or more ID;\nevery ID use the same above many2one notation with external reference.\nValue may be a string (just 1 value) or a list.\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"bank_ids\":\n                    [\n                        \"base.bank_partner_demo\",\n                        \"base_iban.bank_iban_china_export\",\n                    ],\n                \"category_id\": \"base.res_partner_category_0\",\n            }\n        }\n    )\n\n.. note::\n\n    You can also use tha classic Odoo syntax with commands:\n    You can integrate classic Odoo syntax with **z0bug_odoo external** reference.\n\n* [0, 0, values (dict)]               # CREATE record and link\n* [1, ID (int), values (dict)]        # UPDATE linked record\n* [2, ID (int)]                       # DELETE linked record by ID\n* [3, ID (int)]                       # UNLINK record ID (do not delete record)\n* [4, ID (int)]                       # LINK record by ID\n* [5, x] or [5]                       # CLEAR unlink all record IDs\n* [6, x, IDs (list)]                  # SET link record IDs\n\n\n\nbinary\n~~~~~~\n\nBinary file are supplied with os file name. Test environment load file and\nget binary value. File must be located in **tests/data** directory.\n\n::\n\n    self.resource_create(\n        \"res.partner\",\n        xref=\"z0bug.partner1\",\n        values={\n             {\n                ...\n                \"image\": \"z0bug.partner1.png\"\n            }\n        }\n    )\n\n\n\nUseful External Reference\n-------------------------\n\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| id                                     | name                  | model                | note                                           |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| z0bug.coa_bank                         | Bank                  | account.account      | Default bank account                           |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| external.INV                           | Sale journal          | account.journal      | Default sale journal                           |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| external.BILL                          | Purchase journal      | account.journal      | Default purchase journal                       |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| external.MISC                          | Miscellaneous journal | account.journal      | Default miscellaneous journal                  |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| external.BNK1                          | Bank journal          | account.journal      | Default bank journal                           |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| account.account_payment_term_immediate | Immediate Payment     | account.payment.term |                                                |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| account.account_payment_term_net       | 30 Net Days           | account.payment.term |                                                |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| z0bug.tax_22a                          | Purchase 22% VAT      | account.tax          | Italian default purchase VAT rate              |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| z0bug.tax_22v                          | Sale 22% VAT          | account.tax          | Italian default sale VAT rate                  |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| base.main_company                      | Default company       | res.company          | Default company for test                       |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| product.product_category_1             | All / Saleable        | product.category     | Useful product category                        |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| base.USD                               | USD currency          | res.currency         | Test currency during test execution: US dollar |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n| base.user_root                         | Administrator         | res.users            | User under test execution                      |\n+----------------------------------------+-----------------------+----------------------+------------------------------------------------+\n\n\n\nFunctions\n---------\n\ncast_types\n~~~~~~~~~~\n\n**cast_types(self, resource, values, fmt=None, group=None, not_null=False)**\n\nConvert resource fields in appropriate type, based on Odoo type.\n\n| Args:\n|     resource (str): Odoo model name\n|     values (dict): record data\n|     fmt (selection): output format\n|     group (str): used to manager group data; default is \"base\"\n|\n| Returns:\n|     Appropriate values\n\nThe parameter fmt declares the purpose of casting and declare the returned format of\n<2many> fields as follows table:\n\n::\n\n                                    | fmt=='cmd'         | fmt=='id'  | fmt=='py'\n    <2many> [(0|1,x,dict)]          | [(0|1,x,dict)] *   | [dict] *   | [dict] *\n    <2many> [(0|1,x,xref)]          | [(0|1,x,dict)]     | [dict]     | [dict]\n    <2many> [(2|3|4|5,id)]          | as is              | as is      | as is\n    <2many> [(2|3|4|5,xref)]        | [(2|3|4|5,id)]     | as is      | as is\n    <2many> [(6,0,[ids])]           | as is              | [ids]      | [ids]\n    <2many> [(6,0,xref)]            | [(6,0,[id])]       | [id]       | [id]\n    <2many> [(6,0,[xref,...])]      | [(6,0,[ids])]      | [ids]      | [ids]\n    <2many> dict                    | [(0,0,dict)        | [dict]     | [dict]\n    <2many> xref (exists)           | [(6,0,[id])]       | [id]       | [id]\n    <2many> xref (not exists)       | [(0,0,dict)]       | [dict]     | [dict]\n    <2many> [xref] (exists)         | [(6,0,[id])]       | [id]       | [id]\n    <2many> [xref] (not exists)     | [(0,0,dict)]       | [dict]     | [dict]\n    <2many> [xref,...] (exists)     | [(6,0,[ids])]      | [ids]      | [ids]\n    <2many> [xref,...] (not exists) | [(0,0,dict),(...)] | [dict,...] | [dict,...]\n    <2many> [ids] **                | [(6,0,[ids])]      | [ids]      | [ids]\n    <2many> id                      | [(6,0,[id])]       | [id]       | [id]\n    <2many> \"xref,...\" (exists)     | [(6,0,[ids])]      | [ids]      | [ids]\n    <2many> \"xref,...\" (not exists) | [(0,0,dict),(...)] | [dict,...] | [dict,...]\n\n    Caption: dict -> {'a': 'A', ..}, xref -> \"abc.def\", id -> 10, ids -> 1,2,...\n    * fields of dict are recursively processed\n    ** ids 1-6 have processed as Odoo cmd\n\nfmt ==  'cmd' means convert to Odoo API format: <2many> fields are returned with\nprefixed 0|1|2|3|4|5|6 value (read _cast_2many docs).\n\nfmt == 'id' is like 'cmd': prefix are added inside dict not at the beginning.\n\nfmt == 'py' means convert to native python (remove all Odoo command prefixes).\nIt is used for comparison.\n\nWhen no format is required (fmt is None), some conversion may be not applicable:\n\n<many2one> field will be left unchanged when invalid xref is issued and <2many>\nfield me will be left unchanged when one or more invalid xref are issued.\n\nstr, int, long, selection, binary, html fields are always left as is\n\ndate, datetime fields and fmt=='cmd' and python2 (odoo <= 10.0) return ISO format\nmany2one fields, if value is (int|long) are left as is; if value is (xref) the\nid of xref is returned.\n\n.. note::\n\n    Odoo one2many valid cmd are: 0,1 and 2 (not checked)\n\nstore_resource_data\n~~~~~~~~~~~~~~~~~~~\n\n**store_resource_data(self, resource, xref, values, group=None, name=None)**\n\nStore a record data definition for furthermore use.\n\n| Args:\n|     resource (str): Odoo model name\n|     xref (str): external reference\n|     values (dict): record data\n|     group (str): used to manager group data; default is \"base\"\n|     name (str): label of dataset; default is resource name\n\n\nData stored is used by ``setup_env()`` function and/or by:\n\n* ``resource_create()`` without values\n* ``resource_write()`` without values\n* ``resource_make()`` without values\n\n\ncompute_date\n~~~~~~~~~~~~\n\n**compute_date(self, date, refdate=None)**\n\nCompute date or datetime against today or a reference date.\n\n| Args:\n|     date (date or string or integer): text date formula\n|     refdate (date or string): reference date\n\nDate may be:\n\n* python date/datetime value\n* string with ISO format \"YYYY-MM-DD\" or \"YYYY-MM-DD HH:MM:SS\"\n* string value that is a relative date against today or reference date\n\nRelative string format is like ISO, with 3 groups separated by '-' (dash).\nEvery group may be an integer or a special notation:\n\n* starting with '<' meas subtract; i.e. '<2' means minus 2\n* ending with '>' meas add; i.e. '2>' means plus 2\n* '#' with '<' or '>' means 1; i.e. '<###' means minus 1\n* all '#' means same value of reference date\n\nA special notation '+N' and '-N', where N is an integer means add N days\nor subtract N day from reference date.\nHere, in following examples, are used python iso date convention:\n\n* '+N': return date + N days to refdate (python timedelta)\n* '-N': return date - N days from refdate (python timedelta)\n* '%Y-%m-%d': strftime of issued value\n* '%Y-%m-%dT%H:%M:%S': same datetime\n* '%Y-%m-%d %H:%M:%S': same datetime\n* '####-%m-%d': year from refdate (or today), month '%m', day '%d'\n* '####-##-%d': year and month from refdate (or today), day '%d'\n* '2024-##-##': year 2024, month and day from refdate (or today)\n* '<###-%m-%d': year -1  from refdate (or today), month '%m', day '%d'\n* '<001-%m-%d': year -1  from refdate (or today), month '%m', day '%d'\n* '<###-#>-%d': year -1  from refdate, month +1 from refdate, day '%d'\n* '<005-2>-##': year -5, month +2 and day from refdate\n\nNotes:\n    * Returns a ISO format string.\n    * Returned date is a valid date; i.e. '####-#>-31', with ref month January result '####-02-31' becomes '####-03-03'\n    * To force last day of month, set '99': i.e. '####-<#-99' becomes the last day of previous month of refdate\n\n\nresource_browse\n~~~~~~~~~~~~~~~\n\n**resource_browse(self, xref, raise_if_not_found=True, resource=None, group=None)**\n\nBind record by xref, searching it or browsing it.\nThis function returns a record using issued parameters. It works in follow ways:\n\n* With valid xref it work exactly like self.env.ref()\n* If xref is an integer it works exactly like self.browse()\n* I xref is invalid, xref is used to search record\n    * xref is searched in stored data\n    * xref (\"MODULE.NAME\"): if MODULE == \"external\", NAME is the record key\n\n| Args:\n|     xref (str): external reference\n|     raise_if_not_found (bool): raise exception if xref not found or\n|                                if more records found\n|     resource (str): Odoo model name, i.e. \"res.partner\"\n|     group (str): used to manager group data; default is \"base\"\n|\n| Returns:\n|     obj: the Odoo model record\n|\n| Raises:\n|     ValueError: if invalid parameters issued\n\nresource_create\n~~~~~~~~~~~~~~~\n\nCreate a test record and set external ID to next tests.\nThis function works as standard Odoo create() with follow improvements:\n\n* It can create external reference too\n* It can use stored data if no values supplied\n* Use new api even on Odoo 7.0 or less\n\n| Args:\n|     resource (str): Odoo model name, i.e. \"res.partner\"\n|     values (dict): record data (default stored data)\n|     xref (str): external reference to create\n|     group (str): used to manager group data; default is \"base\"\n|\n| Returns:\n|     obj: the Odoo model record, if created\n\n\nresource_write\n~~~~~~~~~~~~~~\n\nUpdate a test record.\nThis function works as standard Odoo write() with follow improvements:\n\n* If resource is a record, xref is ignored (it should be None)\n* It resource is a string, xref must be a valid xref or an integer\n* If values is not supplied, record is restored to stored data values\n\ndef resource_write(self, resource, xref=None, values=None, raise_if_not_found=True, group=None):\n\n    Args:\n        resource (str|obj): Odoo model name or record to update\n        xref (str): external reference to update: required id resource is string\n        values (dict): record data (default stored data)\n        raise_if_not_found (bool): raise exception if xref not found or if more records found\n        group (str): used to manager group data; default is \"base\"\n\n    Returns:\n        obj: the Odoo model record\n\n    Raises:\n        ValueError: if invalid parameters issued\n\nresource_make\n~~~~~~~~~~~~~\n\nCreate or write a test record.\nThis function is a hook to resource_write() or resource_create().\n\ndef resource_make(self, resource, xref, values=None, group=None):\n\ndeclare_resource_data\n~~~~~~~~~~~~~~~~~~~~~\n\nDeclare data to load on setup_env().\n\n| Args:\n|     resource (str): Odoo model name, i.e. \"res.partner\"\n|     data (dict): record data\n|     name (str): label of dataset; default is resource name\n|     group (str): used to manager group data; default is \"base\"\n|     merge (str): values are (\"local\"|\"zerobug\")\n|\n| Raises:\n|     TypeError: if invalid parameters issued\n\ndeclare_all_data\n~~~~~~~~~~~~~~~~\n\nDeclare all data to load on setup_env()\n\n| Args:\n|     message (dict): data message\n|         TEST_SETUP_LIST (list): resource list to load\n|         TEST_* (dict): resource data; * is the uppercase resource name where\n|                        dot are replaced by \"_\"; (see declare_resource_data)\n|     group (str): used to manager group data; default is \"base\"\n|     merge (str): values are (\"local\"|\"zerobug\")\n|     data_dir (str): data directory, default is \"tests/data\"\n|\n| Raises:\n|     TypeError: if invalid parameters issued\n\nget_resource_data\n~~~~~~~~~~~~~~~~~\n\nGet declared resource data; may be used to test compare\n\n| Args:\n|     resource (str): Odoo model name or name assigned, i.e. \"res.partner\"\n|     xref (str): external reference\n|     group (str): if supplied select specific group data; default is \"base\"\n|     try_again (bool): engage conveyed value\n|\n| Returns:\n|     dictionary with data or empty dictionary\n\nget_resource_data_list\n~~~~~~~~~~~~~~~~~~~~~~\n\nGet declared resource data list.\n\ndef get_resource_data_list(self, resource, group=None):\n\n    Args:\n        resource (str): Odoo model name or name assigned, i.e. \"res.partner\"\n        group (str): if supplied select specific group data; default is \"base\"\n\n    Returns:\n        list of data\n\nget_resource_list\n~~~~~~~~~~~~~~~~~\n\nGet declared resource list.\n\ndef get_resource_list(self, group=None):\n\n    Args:\n        group (str): if supplied select specific group data; default is \"base\"\n\nsetup_company\n~~~~~~~~~~~~~\n\nSetup company values for current user.\n\nThis function assigns company to current user and / or can create xref aliases\nand /or can update company values.\nThis function is useful in multi companies tests where different company values\nwill be used in different tests. May be used in more simple test where company\ndata will be updated in different tests.\nYou can assign partner_xref to company base by group; then all tests executed\nafter setup_env(), use the assigned partner data for company of the group.\nYou can also create more companies and assign one of them to test by group.\n\n| Args:\n|     company (obj): company to update; if not supplied a new company is created\n|     xref (str): external reference or alias for main company\n|     partner_xref (str): external reference or alias for main company partner\n|     recv_xref (str): external reference or alias for receivable account\n|     pay_xref (str): external reference or alias for payable account\n|     bnk1_xref (str): external reference or alias for 1st liquidity bank\n|     values (dict): company data to update immediately\n|     group (str): if supplied select specific group data; default is \"base\"\n|\n| Returns:\n|     default company for user\n\nsetup_env\n~~~~~~~~~\n\nCreate all record from declared data.\n\nThis function starts the test workflow creating the test environment.\nTest data must be declared before engage this function by file .csv or\nfile .xlsx or by source declaration TEST_<MODEL>.\n\nsetup_env may be called more times with different group value.\nIf it is called with the same group, it recreates the test environment with\ndeclared values; however this feature might do not work for some reason: i.e.\nif test creates a paid invoice, the setup_env() cannot unlink invoice.\nIf you want to recreate the same test environment, assure the conditions for\nunlink of all created and tested records.\n\nIf you create more test environment with different group you can grow the data\nduring test execution with complex scenario.\nIn this way you can create functional tests not only regression tests.\n\n| Args:\n|     lang (str): install & load specific language\n|     locale (str): install locale module with CoA; i.e l10n_it\n|     group (str): if supplied select specific group data; default is \"base\"\n|     source (str): values are (\"local\"|\"zerobug\")\n|     setup_list (list): list of Odoo modelS; if missed use TEST_SETUP_LIST\n|     data_dir (str): data directory, default is \"tests/data\"\n|\n| Returns:\n|     None\n\nresource_edit\n~~~~~~~~~~~~~\n\nServer-side web form editing.\n\nOrdinary Odoo test use the primitive create() and write() function to manage\ntest data. These methods create an update records, but they do not properly\nreflect the behaviour of user editing form with GUI interface.\n\nThis function simulates the client-side form editing in the server-side.\nIt works in the follow way:\n\n* It can simulate the form create record\n* It can simulate the form update record\n* It can simulate the user data input\n* It calls the onchange functions automatically\n* It may be used to call button in the form\n\nUser action simulation:\n\nThe parameter <web_changes> is a list of user actions to execute sequentially.\nEvery element of the list is another list with 2 or 3 values:\n\n* Field name to assign value\n* Value to assign\n* Optional function to execute (i.e. specific onchange)\n\nIf field is associated to an onchange function the relative onchange functions\nare execute after value assignment. If onchange set another field with another\nonchange the relative another onchange are executed until all onchange are\nexhausted. This behavior is the same of the form editing.\n\nWarning: because function are always executed at the server side the behavior\nmay be slightly different from actual form editing. Please take note of\nfollowing limitations:\n\n* update form cannot simulate discard button\n* some required data in create must be supplied by default parameter\n* form inconsistency cannot be detected by this function\n* nested function must be managed by test code (i.e. wizard from form)\n\nSee test_testenv module for test examples\nhttps://github.com/zeroincombenze/zerobug-test/tree/12.0/test_testenv\n\ndef resource_edit(self, resource, default={}, web_changes=[], actions=[], ctx={}):\n\n    Args:\n        resource (str or obj): if field is a string simulate create web behavior of\n        Odoo model issued in resource;\n        if field is an obj simulate write web behavior on the issued record\n        default (dict): default value to assign\n        web_changes (list): list of tuples (field, value); see <wiz_edit>\n\n    Returns:\n        windows action to execute or obj record\n\nwizard\n~~~~~~\n\nExecute a full wizard.\n\nEngage the specific wizard, simulate user actions and return the wizard result,\nusually a windows action.\n\nIt is useful to test:\n\n    * view names\n    * wizard structure\n    * wizard code\n\nBoth parameters <module> and <action_name> must be issued in order to\ncall <wiz_by_action_name>; they are alternative to act_windows.\n\n*** Example of use ***\n\n::\n\n  XML view file:\n      <record id=\"action_example\" model=\"ir.actions.act_window\">\n          <field name=\"name\">Example</field>\n          <field name=\"res_model\">wizard.example</field>\n          [...]\n      </record>\n\nPython code:\n\n::\n\n    act_windows = self.wizard(module=\"module_example\",\n        action_name=\"action_example\", ...)\n    if self.is_action(act_windows):\n        act_windows = self.wizard(act_windows=act_windows, ...)\n\nUser action simulation:\n\nThe parameter <web_changes> is a list of user actions to execute sequentially.\nEvery element of the list is another list with 2 or 3 values:\n\n* Field name to assign value\n* Value to assign\n* Optional function to execute (i.e. specific onchange)\n\nIf field is associated to an onchange function the relative onchange functions\nare execute after value assignment. If onchange set another field with another\nonchange the relative another onchange are executed until all onchange are\nexhausted. This behavior is the same of the form editing.\n\ndef wizard(self, module=None, action_name=None, act_windows=None, records=None, default=None, ctx={}, button_name=None, web_changes=[], button_ctx={},):\n\n    Args:\n        module (str): module name for wizard to test; if \".\" use current module name\n        action_name (str): action name\n        act_windows (dict): Odoo windows action (do not issue module & action_name)\n        records (obj): objects required by the download wizard\n        default (dict): default value to assign\n        ctx (dict): context to pass to wizard during execution\n        button_name (str): function name to execute at the end of then wizard\n        web_changes (list): list of tuples (field, value); see above\n        button_ctx (dict): context to pass to button_name function\n\n    Returns:\n        result of the wizard\n\n    Raises:\n        ValueError: if invalid parameters issued\n\nvalidate_record\n~~~~~~~~~~~~~~~\n\nValidate records against template values.\nDuring the test will be necessary to check result record values.\nThis function aim to validate all the important values with one step.\nYou have to issue 2 params: template with expected values and record to check.\nYou can declare just some field value in template which are important for you.\nBoth template and record are lists, record may be a record set too.\nThis function do following steps:\n\n* matches templates and record, based on template supplied data\n* check if all template are matched with 1 record to validate\n* execute self.assertEqual() for every field in template\n* check for every template record has matched with assert\n\ndef validate_records(self, template, records):\n\n    Args:\n         template (list of dict): list of dictionaries with expected values\n         records (list or set): records to validate values\n\n    Returns:\n        list of matched coupled (template, record) + # of assertions\n\n    Raises:\n        ValueError: if no enough assertions or one assertion is failed\n\nget_records_from_act_windows\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nGet records from a windows message.\n\ndef get_records_from_act_windows(self, act_windows):\n\n    Args:\n        act_windows (dict): Odoo windows action returned by a wizard\n\n    Returns:\n        records or False\n\n    Raises:\n        ValueError: if invalid parameters issued\n\n\n\nGetting started\n===============\n\n\nPrerequisites\n-------------\n\nZeroincombenze tools requires:\n\n* Linux Centos 7/8 or Debian 9/10 or Ubuntu 18/20/22\n* python 2.7+, some tools require python 3.6+, best python 3.8+\n* bash 5.0+\n\n\n\nInstallation\n------------\n\nFor stable version:\n\n`pip install z0bug_odoo`\n\nFor current version:\n\n`cd $HOME`\n`git@github.com:zeroincombenze/tools.git`\n`cd $HOME/tools`\n`./install_tools.sh`\n\n\n\nUpgrade\n-------\n\nStable version via Python Package\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n    pip install --upgrade z0bug_odoo\n\nCurrent version via Git\n~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n    cd ./tools\n    ./install_tools.sh -pUT\n    source $HOME/devel/activate_tools\n\n\n\nChangeLog History\n-----------------\n\n2.0.19 (2024-05-11)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] Colored test results\n\n2.0.18 (2024-04-17)\n~~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: no data loaded in some cases\n* [FIX[ TestEnv: resource_edit manage read-only fields\n* [QUA] TestEnv coverage 93% by test_test_env in https://github.com/zeroincombenze/zerobug-test\n\n2.0.17 (2024-02-27)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] TestEnv: minor improvements\n* [FIX] TestEnv: crash if no account.journal in data\n* [IMP] Data with date range 2024\n\n2.0.16 (2024-02-17)\n~~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: nested +multi fields with Odoo cmd\n\n2.0.15 (2024-01-27)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] Documentation typo corrections\n* [IMP] Date range file .xlsx for TestEnv\n* [IMP] TestEnv: local data dir new rules\n* [FIX] TestEnv: 3 level xref, sometime fails with \"_\" in module name\n* [FIX] TestEnv: caller environment more than 1 level\n* [FIX] TestEnv: sometime is_action() fails\n* [FIX] TestEnv: wizard active model\n* [FIX] TestEnv: wizard module name is current module under test\n* [IMP] TestEnv: binding model in view for Odoo 11.0+\n* [IMP] TestEnv: write with xref can update xref id\n* [IMP] TestEnv: warning if no setUp() declaration\n* [IMP] TestEnv: resource_download, now default filed name is \"data\"\n\n\n2.0.14 (2023-12-22)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] TestEnv: commit odoo data became internal feature\n* [IMP] TestEnv: test on model asset.asset\n* [IMP] TestEnv: detail external reference coding free\n* [IMP] TestEnv: empty currency_id is set with company currency\n* [FIX] TestEnv: minor fixes in mixed environment excel + zerobug\n* [FIX] TestEnv: sometimes external.KEY did not work\n* [FIX] TestEnv: 3 level xref fails when module ha \"_\" in its name\n* [IMP] _check4deps.py: documentation clearing\n\n2.0.13 (2023-12-01)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] TestEnv: now you can declare you own source data directory\n* [IMP] TestEnv: file account.account.xlsx with l10n_generic_oca + some useful records\n* [IMP] TestEnv: file account.tax.xlsx with some italian taxes for l10n_generic_oca\n* [IMP] TestEnv: simple expression for data value\n\n2.0.12 (2023-09-12)\n~~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: validate_records with 2 identical template records\n\n2.0.10 (2023-07-02)\n~~~~~~~~~~~~~~~~~~~\n\n* [IMP] TestEnv: new feature, external reference with specific field value\n* [REF] TestEnv: tomany casting refactoring\n\n2.0.9 (2023-06-24)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: sometimes, validate_records does not match many2one fields\n* [FIX[ TestEnv: sometime crash in wizard on Odoo 11.0+ due inexistent ir.default\n* [FIX] TestEnv: default value in wizard creation, overlap default function\n* [FIX] TestEnv: record not found for xref of other group\n* [IMP] TestEnv: resource_bind is not more available: it is replaced by resource_browse\n\n2.0.8 (2023-04-26)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: multiple action on the same records\n\n2.0.7 (2023-04-08)\n~~~~~~~~~~~~~~~~~~\n\n* [NEW] TestEnv: assertion counter\n* [IMP] TestEnv: is_xref recognizes dot name, i.e \"zobug.external.10\"\n* [IMP] TestEnv: the field <description> is not mode key (only acount.tax)\n* [IMP] TestEnv: 3th level xref may be a many2one field type\n\n2.0.6 (2023-02-20)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: _get_xref_id recognize any group\n* [FIX] TestEnv: datetime field more precise (always with time)\n* [FIX] TestEnv: resource_make / resource_write fall in crash if repeated on headr/detail models\n* [NEW] TestEnv: 2many fields accepts more xref values\n* [IMP] TestEnv: debug message with more icons and more readable\n* [IMP] TestEnv: cast_types with formatting for python objects\n* [IMP] TestEnv: validate_record now uses intelligent algorithm to match pattern templates and records\n\n2.0.5 (2023-01-25)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: in some rare cases, wizard crashes\n* [NEW] TestEnv: get_records_from_act_windows()\n* [IMP] TestEnv: resource_make now capture demo record if available\n* [IMP] TestEnv: resource is not required for declared xref\n* [IMP] TestEnv: self.module has all information about current testing module\n* [IMP] TestEnv: conveyance functions for all fields (currenly jsust for account.payment.line)\n* [IMP] TestEnv: fields many2one accept object as value\n* [IMP] TestEnv: function validate_records() improvements\n* [FIX] TestEnv: company_setup, now you can declare bank account\n* [IMP] TesEnv: minor improvements\n\n2.0.4 (2023-01-13)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] TestEnv: resource_create does not duplicate record\n* [FIX] TestEnv: resource_write after save calls write() exactly like Odoo behavior\n* [NEW] TestEnv: new function field_download()\n* [NEW] TestEnv: new function validate_records()\n* [IMP] TestEnv: convert_to_write convert binary fields too\n* [IMP] TestEnv: minor improvements\n\n2.0.3 (2022-12-29)\n~~~~~~~~~~~~~~~~~~\n\n* [IMP] TestEnv: more debug messages\n* [IMP] TestEnv: more improvements\n* [FIX] TestEnv: sometime crashes if default use context\n* [FIX] TestEnv: bug fixes\n\n2.0.2 (2022-12-09)\n~~~~~~~~~~~~~~~~~~\n\n* [FIX] Automatic conversion of integer into string for 'char' fields\n* [IMP] TestEnv\n\n2.0.1.1 (2022-11-03)\n~~~~~~~~~~~~~~~~~~~~\n\n* [REF] clone_oca_dependencies.py\n\n2.0.1 (2022-10-20)\n~~~~~~~~~~~~~~~~~~\n\n* [IMP] Stable version\n\n2.0.0.1 (2022-10-15)\n~~~~~~~~~~~~~~~~~~~~\n\n* [FIX] Crash in travis\n\n2.0.0 (2022-08-10)\n~~~~~~~~~~~~~~~~~~\n\n* [REF] Stable version\n\n\n\nCredits\n=======\n\nCopyright\n---------\n\nSHS-AV s.r.l. <https://www.shs-av.com/>\n\n\nAuthors\n-------\n\n* `SHS-AV s.r.l. <https://www.zeroincombenze.it>`__\n\n\n\nContributors\n------------\n\n* `Antonio M. Vigliotti <info@shs-av.com>`__\n* `Antonio Maria Vigliotti <antoniomaria.vigliotti@gmail.com>`__\n\n\n|\n|\n\n.. |Maturity| image:: https://img.shields.io/badge/maturity-Beta-yellow.png\n    :target: https://odoo-community.org/page/development-status\n    :alt: \n.. |license gpl| image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg\n    :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html\n    :alt: License: AGPL-3\n.. |license opl| image:: https://img.shields.io/badge/licence-OPL-7379c3.svg\n    :target: https://www.odoo.com/documentation/user/9.0/legal/licenses/licenses.html\n    :alt: License: OPL\n.. |Tech Doc| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-docs-2.svg\n    :target: https://wiki.zeroincombenze.org/en/Odoo/2.0.19/dev\n    :alt: Technical Documentation\n.. |Help| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-help-2.svg\n    :target: https://wiki.zeroincombenze.org/it/Odoo/2.0.19/man\n    :alt: Technical Documentation\n.. |Try Me| image:: https://www.zeroincombenze.it/wp-content/uploads/ci-ct/prd/button-try-it-2.svg\n    :target: https://erp2.zeroincombenze.it\n    :alt: Try Me\n.. |Zeroincombenze| image:: https://avatars0.githubusercontent.com/u/6972555?s=460&v=4\n   :target: https://www.zeroincombenze.it/\n   :alt: Zeroincombenze\n.. |en| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/flags/en_US.png\n   :target: https://www.facebook.com/Zeroincombenze-Software-gestionale-online-249494305219415/\n.. |it| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/flags/it_IT.png\n   :target: https://www.facebook.com/Zeroincombenze-Software-gestionale-online-249494305219415/\n.. |check| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/check.png\n.. |no_check| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/no_check.png\n.. |menu| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/menu.png\n.. |right_do| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/right_do.png\n.. |exclamation| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/exclamation.png\n.. |warning| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/warning.png\n.. |same| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/same.png\n.. |late| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/late.png\n.. |halt| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/halt.png\n.. |info| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/awesome/info.png\n.. |xml_schema| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/iso/icons/xml-schema.png\n   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/iso/scope/xml-schema.md\n.. |DesktopTelematico| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/ade/icons/DesktopTelematico.png\n   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/ade/scope/Desktoptelematico.md\n.. |FatturaPA| image:: https://raw.githubusercontent.com/zeroincombenze/grymb/master/certificates/ade/icons/fatturapa.png\n   :target: https://github.com/zeroincombenze/grymb/blob/master/certificates/ade/scope/fatturapa.md\n.. |chat_with_us| image:: https://www.shs-av.com/wp-content/chat_with_us.gif\n   :target: https://t.me/Assitenza_clienti_powERP",
    "bugtrack_url": null,
    "license": "Affero GPL",
    "summary": "Odoo testing framework",
    "version": "2.0.19",
    "project_urls": {
        "Changelog": "https://github.com/zeroincombenze/tools/blob/master/z0bug_odoo/egg-info/CHANGELOG.rst",
        "Documentation": "https://zeroincombenze-tools.readthedocs.io/en/latest/z0bug_odoo",
        "Homepage": "https://github.com/zeroincombenze/tools",
        "Source": "https://github.com/zeroincombenze/tools/tree/master/z0bug_odoo"
    },
    "split_keywords": [
        "unit",
        "test",
        "debug"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "367a3581bb09a87c3c6de6959ea379cd6cc35c5580e73ece66e7acce69f23318",
                "md5": "1a505c90f77799a4637456b543130393",
                "sha256": "94637f4918778f4ca6736e29597ce6cd37a9e7ae85e27cc9f25e93f094e3b7aa"
            },
            "downloads": -1,
            "filename": "z0bug_odoo-2.0.19.tar.gz",
            "has_sig": false,
            "md5_digest": "1a505c90f77799a4637456b543130393",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 1601638,
            "upload_time": "2024-05-14T16:53:01",
            "upload_time_iso_8601": "2024-05-14T16:53:01.263080Z",
            "url": "https://files.pythonhosted.org/packages/36/7a/3581bb09a87c3c6de6959ea379cd6cc35c5580e73ece66e7acce69f23318/z0bug_odoo-2.0.19.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-14 16:53:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "zeroincombenze",
    "github_project": "tools",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "lcname": "z0bug-odoo"
}
        
Elapsed time: 0.27515s