mirror of
https://github.com/odoo/documentation.git
synced 2025-12-12 07:29:27 +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#14924
X-original-commit: 33d7058113
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
|
||||
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
|
||||
discuss the practice of testing JS code in isolation: these tests stay in the
|
||||
browser, and are not supposed to reach the server.
|
||||
codebase in Javascript, it is necessary to test it.
|
||||
|
||||
.. _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.
|
||||
QUnit defines the concepts of *tests* and *modules* (a set of related tests),
|
||||
and gives us a web based interface to execute the tests.
|
||||
- :doc:`Web test helpers <../frontend/unit_testing/web_helpers>`
|
||||
|
||||
For example, here is what a pyUtils test could look like:
|
||||
|
||||
.. 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.
|
||||
- :doc:`Mock server <../frontend/unit_testing/mock_server>`
|
||||
|
||||
.. _reference/testing/integration-testing:
|
||||
|
||||
@@ -982,8 +747,6 @@ you can use the :meth:`~odoo.tests.BaseCase.assertQueryCount` method, integrated
|
||||
.. _qunit: https://qunitjs.com/
|
||||
.. _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.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
|
||||
.. _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
|
||||
|
||||
@@ -75,8 +75,8 @@ like this:
|
||||
'web/static/src/js/webclient.js',
|
||||
'web/static/src/xml/webclient.xml',
|
||||
],
|
||||
'web.qunit_suite_tests': [
|
||||
'web/static/src/js/webclient_tests.js',
|
||||
'web.assets_unit_tests': [
|
||||
'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
|
||||
website: ecommerce, portal, forum, blog, ...
|
||||
|
||||
- `web.qunit_suite_tests`: all javascript qunit testing code (tests, helpers, mocks)
|
||||
|
||||
- `web.qunit_mobile_suite_tests`: mobile specific qunit testing code
|
||||
|
||||
- `web.assets_unit_tests`: all javascript unit testing code (tests, helpers, mocks)
|
||||
|
||||
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:
|
||||
|
||||
- :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`;
|
||||
- **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 hooks like `after` and `afterEach`
|
||||
|
||||
Reference in New Issue
Block a user