@@ -281,252 +281,17 @@ Testing JS code
281281
282282Testing a complex system is an important safeguard to prevent regressions and to
283283guarantee that some basic functionality still works. Since Odoo has a non trivial
284- codebase in Javascript, it is necessary to test it. In this section, we will
285- discuss the practice of testing JS code in isolation: these tests stay in the
286- browser, and are not supposed to reach the server.
284+ codebase in Javascript, it is necessary to test it.
287285
288- .. _reference/testing/qunit :
286+ See the :doc: `Unit testing <../frontend/unit_testing >` to learn about the
287+ various aspect of the front-end testing framework, or jump directly to one of the
288+ sub-sections:
289289
290- Qunit test suite
291- ----------------
290+ - :doc: `Hoot <../frontend/unit_testing/hoot >`
292291
293- The Odoo framework uses the QUnit _ library testing framework as a test runner.
294- QUnit defines the concepts of *tests * and *modules * (a set of related tests),
295- and gives us a web based interface to execute the tests.
292+ - :doc: `Web test helpers <../frontend/unit_testing/web_helpers >`
296293
297- For example, here is what a pyUtils test could look like:
298-
299- .. code-block :: javascript
300-
301- QUnit .module (' py_utils' );
302-
303- QUnit .test (' simple arithmetic' , function (assert ) {
304- assert .expect (2 );
305-
306- var result = pyUtils .py_eval (" 1 + 2" );
307- assert .strictEqual (result, 3 , " should properly evaluate sum" );
308- result = pyUtils .py_eval (" 42 % 5" );
309- assert .strictEqual (result, 2 , " should properly evaluate modulo operator" );
310- });
311-
312- The main way to run the test suite is to have a running Odoo server, then
313- navigate a web browser to ``/web/tests ``. The test suite will then be executed
314- by the web browser Javascript engine.
315-
316- .. image :: testing/tests.png
317- :align: center
318-
319- The web UI has many useful features: it can run only some submodules, or
320- filter tests that match a string. It can show every assertions, failed or passed,
321- rerun specific tests, ...
322-
323- .. warning ::
324-
325- While the test suite is running, make sure that:
326-
327- - your browser window is focused,
328- - it is not zoomed in/out. It needs to have exactly 100% zoom level.
329-
330- If this is not the case, some tests will fail, without a proper explanation.
331-
332- Testing Infrastructure
333- ----------------------
334-
335- Here is a high level overview of the most important parts of the testing
336- infrastructure:
337-
338- - there is an asset bundle named `web.qunit_suite `_. This bundle contains
339- the main code (assets common + assets backend), some libraries, the QUnit test
340- runner and the test bundles listed below.
341-
342- - a bundle named `web.tests_assets `_ includes most of the assets and utils required
343- by the test suite: custom QUnit asserts, test helpers, lazy loaded assets, etc.
344-
345- - another asset bundle, `web.qunit_suite_tests `_, contains all the test scripts.
346- This is typically where the test files are added to the suite.
347-
348- - there is a `controller `_ in web, mapped to the route */web/tests *. This controller
349- simply renders the *web.qunit_suite * template.
350-
351- - to execute the tests, one can simply point its browser to the route */web/tests *.
352- In that case, the browser will download all assets, and QUnit will take over.
353-
354- - there is some code in `qunit_config.js `_ which logs in the console some
355- information when a test passes or fails.
356-
357- - we want the runbot to also run these tests, so there is a test (in `test_js.py `_)
358- which simply spawns a browser and points it to the *web/tests * url. Note that
359- the browser_js method spawns a Chrome headless instance.
360-
361-
362- Modularity and testing
363- ----------------------
364-
365- With the way Odoo is designed, any addon can modify the behaviour of other parts
366- of the system. For example, the *voip * addon can modify the *FieldPhone * widget
367- to use extra features. This is not really good from the perspective of the
368- testing system, since this means that a test in the addon web will fail whenever
369- the voip addon is installed (note that the runbot runs the tests with all addons
370- installed).
371-
372- At the same time, our testing system is good, because it can detect whenever
373- another module breaks some core functionality. There is no complete solution to
374- this issue. For now, we solve this on a case by case basis.
375-
376- Usually, it is not a good idea to modify some other behaviour. For our voip
377- example, it is certainly cleaner to add a new *FieldVOIPPhone * widget and
378- modify the few views that needs it. This way, the *FieldPhone * widget is not
379- impacted, and both can be tested.
380-
381- Adding a new test case
382- ----------------------
383-
384- Let us assume that we are maintaining an addon *my_addon *, and that we
385- want to add a test for some javascript code (for example, some utility function
386- myFunction, located in *my_addon.utils *). The process to add a new test case is
387- the following:
388-
389- 1. create a new file *my_addon/static/tests/utils_tests.js *. This file contains the basic code to
390- add a QUnit module *my_addon > utils *.
391-
392- .. code-block :: javascript
393-
394- odoo .define (' my_addon.utils_tests' , function (require ) {
395- " use strict" ;
396-
397- var utils = require (' my_addon.utils' );
398-
399- QUnit .module (' my_addon' , {}, function () {
400-
401- QUnit .module (' utils' );
402-
403- });
404- });
405-
406-
407- 2. In *my_addon/assets.xml *, add the file to the main test assets:
408-
409- .. code-block :: xml
410-
411- <?xml version =" 1.0" encoding =" utf-8" ?>
412- <odoo >
413- <template id =" qunit_suite_tests" name =" my addon tests" inherit_id =" web.qunit_suite_tests" >
414- <xpath expr =" //script[last()]" position =" after" >
415- <script type =" text/javascript" src =" /my_addon/static/tests/utils_tests.js" />
416- </xpath >
417- </template >
418- </odoo >
419-
420- 3. Restart the server and update *my_addon *, or do it from the interface (to
421- make sure the new test file is loaded)
422-
423- 4. Add a test case after the definition of the *utils * sub test suite:
424-
425- .. code-block :: javascript
426-
427- QUnit .test (" some test case that we want to test" , function (assert ) {
428- assert .expect (1 );
429-
430- var result = utils .myFunction (someArgument);
431- assert .strictEqual (result, expectedResult);
432- });
433-
434- 5. Visit */web/tests/ * to make sure the test is executed
435-
436- Helper functions and specialized assertions
437- -------------------------------------------
438-
439- Without help, it is quite difficult to test some parts of Odoo. In particular,
440- views are tricky, because they communicate with the server and may perform many
441- rpcs, which needs to be mocked. This is why we developed some specialized
442- helper functions, located in `test_utils.js `_.
443-
444- - Mock test functions: these functions help setting up a test environment. The
445- most important use case is mocking the answers given by the Odoo server. These
446- functions use a `mock server `_. This is a javascript class that simulates
447- answers to the most common model methods: read, search_read, nameget, ...
448-
449- - DOM helpers: useful to simulate events/actions on some specific target. For
450- example, testUtils.dom.click performs a click on a target. Note that it is
451- safer than doing it manually, because it also checks that the target exists,
452- and is visible.
453-
454- - create helpers: they are probably the most important functions exported by
455- `test_utils.js `_. These helpers are useful to create a widget, with a mock
456- environment, and a lot of small detail to simulate as much as possible the
457- real conditions. The most important is certainly `createView `_.
458-
459- - `qunit assertions `_: QUnit can be extended with specialized assertions. For
460- Odoo, we frequently test some DOM properties. This is why we made some
461- assertions to help with that. For example, the *containsOnce * assertion takes
462- a widget/jQuery/HtmlElement and a selector, then checks if the target contains
463- exactly one match for the css selector.
464-
465- For example, with these helpers, here is what a simple form test could look like:
466-
467- .. code-block :: javascript
468-
469- QUnit .test (' simple group rendering' , function (assert ) {
470- assert .expect (1 );
471-
472- var form = testUtils .createView ({
473- View: FormView,
474- model: ' partner' ,
475- data: this .data ,
476- arch: ' <form string="Partners">' +
477- ' <group>' +
478- ' <field name="foo"/>' +
479- ' </group>' +
480- ' </form>' ,
481- res_id: 1 ,
482- });
483-
484- assert .containsOnce (form, ' table.o_inner_group' );
485-
486- form .destroy ();
487- });
488-
489- Notice the use of the testUtils.createView helper and of the containsOnce
490- assertion. Also, the form controller was properly destroyed at the end of
491- the test.
492-
493- Best Practices
494- --------------
495-
496- In no particular order:
497-
498- - all test files should be added in *some_addon/static/tests/ *
499- - for bug fixes, make sure that the test fails without the bug fix, and passes
500- with it. This ensures that it actually works.
501- - try to have the minimal amount of code necessary for the test to work.
502- - usually, two small tests are better than one large test. A smaller test is
503- easier to understand and to fix.
504- - always cleanup after a test. For example, if your test instantiates a widget,
505- it should destroy it at the end.
506- - no need to have full and complete code coverage. But adding a few tests helps
507- a lot: it makes sure that your code is not completely broken, and whenever a
508- bug is fixed, it is really much easier to add a test to an existing test suite.
509- - if you want to check some negative assertion (for example, that a HtmlElement
510- does not have a specific css class), then try to add the positive assertion in
511- the same test (for example, by doing an action that changes the state). This
512- will help avoid the test to become dead in the future (for example, if the css
513- class is changed).
514-
515- Tips
516- ----
517-
518- - running only one test: you can (temporarily!) change the *QUnit.test(...) *
519- definition into *QUnit.only(...) *. This is useful to make sure that QUnit
520- only runs this specific test.
521- - debug flag: most create utility functions have a debug mode (activated by the
522- debug: true parameter). In that case, the target widget will be put in the DOM
523- instead of the hidden qunit specific fixture, and more information will be
524- logged. For example, all mocked network communications will be available in the
525- console.
526- - when working on a failing test, it is common to add the debug flag, then
527- comment the end of the test (in particular, the destroy call). With this, it
528- is possible to see the state of the widget directly, and even better, to
529- manipulate the widget by clicking/interacting with it.
294+ - :doc: `Mock server <../frontend/unit_testing/mock_server >`
530295
531296.. _reference/testing/integration-testing :
532297
@@ -982,8 +747,6 @@ you can use the :meth:`~odoo.tests.BaseCase.assertQueryCount` method, integrated
982747 .. _qunit : https://qunitjs.com/
983748.. _qunit_config.js : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_config.js#L49
984749.. _web.tests_assets : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L594
985- .. _web.qunit_suite : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L660
986- .. _web.qunit_suite_tests : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L680
987750.. _controller : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/controllers/main.py#L637
988751.. _test_js.py : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/tests/test_js.py#L13
989752.. _test_utils.js : https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js
0 commit comments