mirror of
https://github.com/odoo/documentation.git
synced 2025-12-13 07:59:10 +07:00
[FIX] web: remove/update old QUnit documentation
This commit replaces the outdated QUnit documentation with links to the freshly written Hoot/web testing documentation. closes odoo/documentation#14832 Signed-off-by: Julien Mougenot (jum) <jum@odoo.com>
This commit is contained in:
@@ -281,252 +281,17 @@ Testing JS code
|
|||||||
|
|
||||||
Testing a complex system is an important safeguard to prevent regressions and to
|
Testing a complex system is an important safeguard to prevent regressions and to
|
||||||
guarantee that some basic functionality still works. Since Odoo has a non trivial
|
guarantee that some basic functionality still works. Since Odoo has a non trivial
|
||||||
codebase in Javascript, it is necessary to test it. In this section, we will
|
codebase in Javascript, it is necessary to test it.
|
||||||
discuss the practice of testing JS code in isolation: these tests stay in the
|
|
||||||
browser, and are not supposed to reach the server.
|
|
||||||
|
|
||||||
.. _reference/testing/qunit:
|
See the :doc:`Unit testing <../frontend/unit_testing>` to learn about the
|
||||||
|
various aspect of the front-end testing framework, or jump directly to one of the
|
||||||
|
sub-sections:
|
||||||
|
|
||||||
Qunit test suite
|
- :doc:`Hoot <../frontend/unit_testing/hoot>`
|
||||||
----------------
|
|
||||||
|
|
||||||
The Odoo framework uses the QUnit_ library testing framework as a test runner.
|
- :doc:`Web test helpers <../frontend/unit_testing/web_helpers>`
|
||||||
QUnit defines the concepts of *tests* and *modules* (a set of related tests),
|
|
||||||
and gives us a web based interface to execute the tests.
|
|
||||||
|
|
||||||
For example, here is what a pyUtils test could look like:
|
- :doc:`Mock server <../frontend/unit_testing/mock_server>`
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
QUnit.module('py_utils');
|
|
||||||
|
|
||||||
QUnit.test('simple arithmetic', function (assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
|
|
||||||
var result = pyUtils.py_eval("1 + 2");
|
|
||||||
assert.strictEqual(result, 3, "should properly evaluate sum");
|
|
||||||
result = pyUtils.py_eval("42 % 5");
|
|
||||||
assert.strictEqual(result, 2, "should properly evaluate modulo operator");
|
|
||||||
});
|
|
||||||
|
|
||||||
The main way to run the test suite is to have a running Odoo server, then
|
|
||||||
navigate a web browser to ``/web/tests``. The test suite will then be executed
|
|
||||||
by the web browser Javascript engine.
|
|
||||||
|
|
||||||
.. image:: testing/tests.png
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
The web UI has many useful features: it can run only some submodules, or
|
|
||||||
filter tests that match a string. It can show every assertions, failed or passed,
|
|
||||||
rerun specific tests, ...
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
While the test suite is running, make sure that:
|
|
||||||
|
|
||||||
- your browser window is focused,
|
|
||||||
- it is not zoomed in/out. It needs to have exactly 100% zoom level.
|
|
||||||
|
|
||||||
If this is not the case, some tests will fail, without a proper explanation.
|
|
||||||
|
|
||||||
Testing Infrastructure
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Here is a high level overview of the most important parts of the testing
|
|
||||||
infrastructure:
|
|
||||||
|
|
||||||
- there is an asset bundle named `web.qunit_suite`_. This bundle contains
|
|
||||||
the main code (assets common + assets backend), some libraries, the QUnit test
|
|
||||||
runner and the test bundles listed below.
|
|
||||||
|
|
||||||
- a bundle named `web.tests_assets`_ includes most of the assets and utils required
|
|
||||||
by the test suite: custom QUnit asserts, test helpers, lazy loaded assets, etc.
|
|
||||||
|
|
||||||
- another asset bundle, `web.qunit_suite_tests`_, contains all the test scripts.
|
|
||||||
This is typically where the test files are added to the suite.
|
|
||||||
|
|
||||||
- there is a `controller`_ in web, mapped to the route */web/tests*. This controller
|
|
||||||
simply renders the *web.qunit_suite* template.
|
|
||||||
|
|
||||||
- to execute the tests, one can simply point its browser to the route */web/tests*.
|
|
||||||
In that case, the browser will download all assets, and QUnit will take over.
|
|
||||||
|
|
||||||
- there is some code in `qunit_config.js`_ which logs in the console some
|
|
||||||
information when a test passes or fails.
|
|
||||||
|
|
||||||
- we want the runbot to also run these tests, so there is a test (in `test_js.py`_)
|
|
||||||
which simply spawns a browser and points it to the *web/tests* url. Note that
|
|
||||||
the browser_js method spawns a Chrome headless instance.
|
|
||||||
|
|
||||||
|
|
||||||
Modularity and testing
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
With the way Odoo is designed, any addon can modify the behaviour of other parts
|
|
||||||
of the system. For example, the *voip* addon can modify the *FieldPhone* widget
|
|
||||||
to use extra features. This is not really good from the perspective of the
|
|
||||||
testing system, since this means that a test in the addon web will fail whenever
|
|
||||||
the voip addon is installed (note that the runbot runs the tests with all addons
|
|
||||||
installed).
|
|
||||||
|
|
||||||
At the same time, our testing system is good, because it can detect whenever
|
|
||||||
another module breaks some core functionality. There is no complete solution to
|
|
||||||
this issue. For now, we solve this on a case by case basis.
|
|
||||||
|
|
||||||
Usually, it is not a good idea to modify some other behaviour. For our voip
|
|
||||||
example, it is certainly cleaner to add a new *FieldVOIPPhone* widget and
|
|
||||||
modify the few views that needs it. This way, the *FieldPhone* widget is not
|
|
||||||
impacted, and both can be tested.
|
|
||||||
|
|
||||||
Adding a new test case
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Let us assume that we are maintaining an addon *my_addon*, and that we
|
|
||||||
want to add a test for some javascript code (for example, some utility function
|
|
||||||
myFunction, located in *my_addon.utils*). The process to add a new test case is
|
|
||||||
the following:
|
|
||||||
|
|
||||||
1. create a new file *my_addon/static/tests/utils_tests.js*. This file contains the basic code to
|
|
||||||
add a QUnit module *my_addon > utils*.
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
odoo.define('my_addon.utils_tests', function (require) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var utils = require('my_addon.utils');
|
|
||||||
|
|
||||||
QUnit.module('my_addon', {}, function () {
|
|
||||||
|
|
||||||
QUnit.module('utils');
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
2. In *my_addon/assets.xml*, add the file to the main test assets:
|
|
||||||
|
|
||||||
.. code-block:: xml
|
|
||||||
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<odoo>
|
|
||||||
<template id="qunit_suite_tests" name="my addon tests" inherit_id="web.qunit_suite_tests">
|
|
||||||
<xpath expr="//script[last()]" position="after">
|
|
||||||
<script type="text/javascript" src="/my_addon/static/tests/utils_tests.js"/>
|
|
||||||
</xpath>
|
|
||||||
</template>
|
|
||||||
</odoo>
|
|
||||||
|
|
||||||
3. Restart the server and update *my_addon*, or do it from the interface (to
|
|
||||||
make sure the new test file is loaded)
|
|
||||||
|
|
||||||
4. Add a test case after the definition of the *utils* sub test suite:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
QUnit.test("some test case that we want to test", function (assert) {
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
var result = utils.myFunction(someArgument);
|
|
||||||
assert.strictEqual(result, expectedResult);
|
|
||||||
});
|
|
||||||
|
|
||||||
5. Visit */web/tests/* to make sure the test is executed
|
|
||||||
|
|
||||||
Helper functions and specialized assertions
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
Without help, it is quite difficult to test some parts of Odoo. In particular,
|
|
||||||
views are tricky, because they communicate with the server and may perform many
|
|
||||||
rpcs, which needs to be mocked. This is why we developed some specialized
|
|
||||||
helper functions, located in `test_utils.js`_.
|
|
||||||
|
|
||||||
- Mock test functions: these functions help setting up a test environment. The
|
|
||||||
most important use case is mocking the answers given by the Odoo server. These
|
|
||||||
functions use a `mock server`_. This is a javascript class that simulates
|
|
||||||
answers to the most common model methods: read, search_read, nameget, ...
|
|
||||||
|
|
||||||
- DOM helpers: useful to simulate events/actions on some specific target. For
|
|
||||||
example, testUtils.dom.click performs a click on a target. Note that it is
|
|
||||||
safer than doing it manually, because it also checks that the target exists,
|
|
||||||
and is visible.
|
|
||||||
|
|
||||||
- create helpers: they are probably the most important functions exported by
|
|
||||||
`test_utils.js`_. These helpers are useful to create a widget, with a mock
|
|
||||||
environment, and a lot of small detail to simulate as much as possible the
|
|
||||||
real conditions. The most important is certainly `createView`_.
|
|
||||||
|
|
||||||
- `qunit assertions`_: QUnit can be extended with specialized assertions. For
|
|
||||||
Odoo, we frequently test some DOM properties. This is why we made some
|
|
||||||
assertions to help with that. For example, the *containsOnce* assertion takes
|
|
||||||
a widget/jQuery/HtmlElement and a selector, then checks if the target contains
|
|
||||||
exactly one match for the css selector.
|
|
||||||
|
|
||||||
For example, with these helpers, here is what a simple form test could look like:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
QUnit.test('simple group rendering', function (assert) {
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
var form = testUtils.createView({
|
|
||||||
View: FormView,
|
|
||||||
model: 'partner',
|
|
||||||
data: this.data,
|
|
||||||
arch: '<form string="Partners">' +
|
|
||||||
'<group>' +
|
|
||||||
'<field name="foo"/>' +
|
|
||||||
'</group>' +
|
|
||||||
'</form>',
|
|
||||||
res_id: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.containsOnce(form, 'table.o_inner_group');
|
|
||||||
|
|
||||||
form.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
Notice the use of the testUtils.createView helper and of the containsOnce
|
|
||||||
assertion. Also, the form controller was properly destroyed at the end of
|
|
||||||
the test.
|
|
||||||
|
|
||||||
Best Practices
|
|
||||||
--------------
|
|
||||||
|
|
||||||
In no particular order:
|
|
||||||
|
|
||||||
- all test files should be added in *some_addon/static/tests/*
|
|
||||||
- for bug fixes, make sure that the test fails without the bug fix, and passes
|
|
||||||
with it. This ensures that it actually works.
|
|
||||||
- try to have the minimal amount of code necessary for the test to work.
|
|
||||||
- usually, two small tests are better than one large test. A smaller test is
|
|
||||||
easier to understand and to fix.
|
|
||||||
- always cleanup after a test. For example, if your test instantiates a widget,
|
|
||||||
it should destroy it at the end.
|
|
||||||
- no need to have full and complete code coverage. But adding a few tests helps
|
|
||||||
a lot: it makes sure that your code is not completely broken, and whenever a
|
|
||||||
bug is fixed, it is really much easier to add a test to an existing test suite.
|
|
||||||
- if you want to check some negative assertion (for example, that a HtmlElement
|
|
||||||
does not have a specific css class), then try to add the positive assertion in
|
|
||||||
the same test (for example, by doing an action that changes the state). This
|
|
||||||
will help avoid the test to become dead in the future (for example, if the css
|
|
||||||
class is changed).
|
|
||||||
|
|
||||||
Tips
|
|
||||||
----
|
|
||||||
|
|
||||||
- running only one test: you can (temporarily!) change the *QUnit.test(...)*
|
|
||||||
definition into *QUnit.only(...)*. This is useful to make sure that QUnit
|
|
||||||
only runs this specific test.
|
|
||||||
- debug flag: most create utility functions have a debug mode (activated by the
|
|
||||||
debug: true parameter). In that case, the target widget will be put in the DOM
|
|
||||||
instead of the hidden qunit specific fixture, and more information will be
|
|
||||||
logged. For example, all mocked network communications will be available in the
|
|
||||||
console.
|
|
||||||
- when working on a failing test, it is common to add the debug flag, then
|
|
||||||
comment the end of the test (in particular, the destroy call). With this, it
|
|
||||||
is possible to see the state of the widget directly, and even better, to
|
|
||||||
manipulate the widget by clicking/interacting with it.
|
|
||||||
|
|
||||||
.. _reference/testing/integration-testing:
|
.. _reference/testing/integration-testing:
|
||||||
|
|
||||||
@@ -982,8 +747,6 @@ you can use the :meth:`~odoo.tests.BaseCase.assertQueryCount` method, integrated
|
|||||||
.. _qunit: https://qunitjs.com/
|
.. _qunit: https://qunitjs.com/
|
||||||
.. _qunit_config.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_config.js#L49
|
.. _qunit_config.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_config.js#L49
|
||||||
.. _web.tests_assets: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L594
|
.. _web.tests_assets: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L594
|
||||||
.. _web.qunit_suite: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L660
|
|
||||||
.. _web.qunit_suite_tests: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L680
|
|
||||||
.. _controller: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/controllers/main.py#L637
|
.. _controller: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/controllers/main.py#L637
|
||||||
.. _test_js.py: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/tests/test_js.py#L13
|
.. _test_js.py: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/tests/test_js.py#L13
|
||||||
.. _test_utils.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js
|
.. _test_utils.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ like this:
|
|||||||
'web/static/src/js/webclient.js',
|
'web/static/src/js/webclient.js',
|
||||||
'web/static/src/xml/webclient.xml',
|
'web/static/src/xml/webclient.xml',
|
||||||
],
|
],
|
||||||
'web.qunit_suite_tests': [
|
'web.assets_unit_tests': [
|
||||||
'web/static/src/js/webclient_tests.js',
|
'web/static/src/js/webclient.test.js',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -94,10 +94,7 @@ know:
|
|||||||
- `web.assets_frontend`: this bundle is about all that is specific to the public
|
- `web.assets_frontend`: this bundle is about all that is specific to the public
|
||||||
website: ecommerce, portal, forum, blog, ...
|
website: ecommerce, portal, forum, blog, ...
|
||||||
|
|
||||||
- `web.qunit_suite_tests`: all javascript qunit testing code (tests, helpers, mocks)
|
- `web.assets_unit_tests`: all javascript unit testing code (tests, helpers, mocks)
|
||||||
|
|
||||||
- `web.qunit_mobile_suite_tests`: mobile specific qunit testing code
|
|
||||||
|
|
||||||
|
|
||||||
Operations
|
Operations
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ key features are:
|
|||||||
|
|
||||||
As such, it has been integrated as a :file:`lib/` in the Odoo codebase and exports 2 main modules:
|
As such, it has been integrated as a :file:`lib/` in the Odoo codebase and exports 2 main modules:
|
||||||
|
|
||||||
- :file:`@odoo/hoot-dom`: (can be used in production code) helpers to:
|
- :file:`@odoo/hoot-dom`: (can be used in tours) helpers to:
|
||||||
|
|
||||||
- **interact** with the DOM, such as :js:meth:`click` and :js:meth:`press`;
|
- **interact** with the DOM, such as :js:meth:`click` and :js:meth:`press`;
|
||||||
- **query** elements from the DOM, such as :js:meth:`queryAll` and :js:meth:`waitFor`;
|
- **query** elements from the DOM, such as :js:meth:`queryAll` and :js:meth:`waitFor`;
|
||||||
|
|
||||||
- :file:`@odoo/hoot`: (only to be used in tests) all the test framework features:
|
- :file:`@odoo/hoot`: (only to be used in unit tests) all the test framework features:
|
||||||
|
|
||||||
- `test`, `describe` and `expect`
|
- `test`, `describe` and `expect`
|
||||||
- test hooks like `after` and `afterEach`
|
- test hooks like `after` and `afterEach`
|
||||||
|
|||||||
Reference in New Issue
Block a user