[WIP] testing in module guide

bzr revid: xmo@openerp.com-20130218084728-q92rcstsr2ca8pqa
This commit is contained in:
Xavier Morel
2013-02-18 09:47:28 +01:00
parent f25c4725b8
commit 5e19bfd47b
9 changed files with 209 additions and 1 deletions
+88
View File
@@ -303,6 +303,94 @@ Note that we're only displaying the record once we know it's been
saved from the database (the ``create`` call has returned without
error).
Mic check, is this working?
---------------------------
So far, features have been implemented, code has been worked and
tentatively tried. However, there is no guarantee they will *keep
working* as new changes are performed, new features added, …
The original author (you, dear reader) could keep a notebook with a
list of workflows to check, to ensure everything keeps working. And
follow the notebook day after day, every time something is changed in
the module.
That gets repetitive after a while. And computers are good at doing
repetitive stuff, as long as you tell them how to do it.
So let's add test to the module, so that in the future the computer
can take care of ensuring what works today keeps working tomorrow.
.. note::
Here we're writing tests after having implemented the widget. This
may or may not work, we may need to alter bits and pieces of code
to get them in a testable state. An other testing methodology is
:abbr:`TDD (Test-Driven Development)` where the tests are written
first, and the code necessary to make these tests pass is written
afterwards.
Both methods have their opponents and detractors, advantages and
inconvenients. Pick the one you prefer.
The first step of :doc:`testing` is to set up the basic testing
structure:
1. Creating a javascript file
.. patch::
2. Containing a test section (and a few tests to make sure the tests
are correctly run)
.. patch::
3. Then declaring the test file in the module's manifest
.. patch::
4. And finally — after restarting OpenERP — navigating to the test
runner at ``/web/tests`` and selecting your soon-to-be-tested
module:
.. image:: module/testing_0.png
:align: center
the testing result do indeed match the test.
The simplest tests to write are for synchronous pure
functions. Synchronous means no RPC call or any other such thing
(e.g. ``setTimeout``), only direct data processing, and pure means no
side-effect: the function takes some input, manipulates it and yields
an output.
In our widget, only ``format_time`` fits the bill: it takes a duration
(in milliseconds) and returns an ``hours:minutes:second`` formatting
of it. Let's test it:
.. patch::
This series of simple tests passes with no issue. The next easy-ish
test type is to test basic DOM alterations from provided input, such
as (for our widget) updating the counter or displaying a record to the
records list: while it's not pure (it alters the DOM "in-place") it
has well-delimited side-effects and these side-effects come solely
from the provided input.
Because these methods alter the widget's DOM, the widget needs a
DOM. Looking up :doc:`a widget's lifecycle <widget>`, the widget
really only gets its DOM when adding it to the document. However a
side-effect of this is to :js:func:`~openerp.web.Widget.start` it,
which for us means going to query the user's times.
We don't have any records to get in our test, and we don't want to
test the initialization yet! So let's cheat a bit: we can manually
:js:func:`set a widget's DOM <openerp.web.Widget.setElement`, let's
create a basic DOM matching what each method expects then call the
method:
.. patch::
.. [#DOM-building] they are not alternative solutions: they work very
well together. Templates are used to build "just
DOM", sub-widgets are used to build DOM subsections
+6
View File
@@ -0,0 +1,6 @@
Index: web_example/static/src/tests/timer.js
===================================================================
--- /dev/null
+++ web_example/static/src/tests/timer.js
@@ -0,0 +1 @@
+
+14
View File
@@ -0,0 +1,14 @@
Index: web_example/static/src/tests/timer.js
===================================================================
--- web_example.orig/static/src/tests/timer.js
+++ web_example/static/src/tests/timer.js
@@ -1 +1,8 @@
-
+openerp.testing.section('timer', function (test) {
+ test('successful test', function () {
+ ok(true, "should work");
+ });
+ test('unsuccessful test', function () {
+ ok(false, "shoud fail");
+ });
+});
+10
View File
@@ -0,0 +1,10 @@
Index: web_example/__openerp__.py
===================================================================
--- web_example.orig/__openerp__.py
+++ web_example/__openerp__.py
@@ -8,4 +8,5 @@
'js': ['static/src/js/first_module.js'],
'css': ['static/src/css/web_example.css'],
'qweb': ['static/src/xml/web_example.xml'],
+ 'test': ['static/src/tests/timer.js'],
}
+55
View File
@@ -0,0 +1,55 @@
Index: web_example/static/src/tests/timer.js
===================================================================
--- web_example.orig/static/src/tests/timer.js
+++ web_example/static/src/tests/timer.js
@@ -1,8 +1,45 @@
openerp.testing.section('timer', function (test) {
- test('successful test', function () {
- ok(true, "should work");
- });
- test('unsuccessful test', function () {
- ok(false, "shoud fail");
+ test('format_time', function (instance) {
+ var w = new instance.web_example.Action();
+
+ strictEqual(
+ w.format_time(0),
+ '00:00:00');
+ strictEqual(
+ w.format_time(543),
+ '00:00:00',
+ "should round sub-second times down to zero");
+ strictEqual(
+ w.format_time(5340),
+ '00:00:05',
+ "should floor sub-second extents to the previous second");
+ strictEqual(
+ w.format_time(60000),
+ '00:01:00');
+ strictEqual(
+ w.format_time(3600000),
+ '01:00:00');
+ strictEqual(
+ w.format_time(86400000),
+ '24:00:00');
+ strictEqual(
+ w.format_time(604800000),
+ '168:00:00');
+
+ strictEqual(
+ w.format_time(22733958),
+ '06:18:53');
+ strictEqual(
+ w.format_time(41676639),
+ '11:34:36');
+ strictEqual(
+ w.format_time(57802094),
+ '16:03:22');
+ strictEqual(
+ w.format_time(73451828),
+ '20:24:11');
+ strictEqual(
+ w.format_time(84092336),
+ '23:21:32');
});
});
+30
View File
@@ -0,0 +1,30 @@
Index: web_example/static/src/tests/timer.js
===================================================================
--- web_example.orig/static/src/tests/timer.js
+++ web_example/static/src/tests/timer.js
@@ -42,4 +42,25 @@ openerp.testing.section('timer', functio
w.format_time(84092336),
'23:21:32');
});
+ test('update_counter', function (instance, $fixture) {
+ var w = new instance.web_example.Action();
+ // $fixture is a DOM tree whose content gets cleaned up before
+ // each test, so we can add whatever we need to it
+ $fixture.append('<div class="oe_web_example_timer">');
+ // Then set it on the widget
+ w.setElement($fixture);
+
+ // Update the counter with a known value
+ w.update_counter(22733958);
+ // And check the DOM matches
+ strictEqual($fixture.text(), '06:18:53');
+
+ w.update_counter(73451828)
+ strictEqual($fixture.text(), '20:24:11');
+ });
+ test('display_record', function (instance, $fixture) {
+ var w = new instance.web_example.Action();
+ $fixture.append('<ol class="oe_web_example_saved">')
+ ok(false, "test display records");
+ });
});
+5
View File
@@ -17,3 +17,8 @@
19
20
21
22
23
24
25
26
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+1 -1
View File
@@ -93,7 +93,7 @@ The DOM root can also be defined programmatically by overridding
Any override to :js:func:`~openerp.web.Widget.renderElement` which
does not call its ``_super`` **must** call
:js:func:`~openerp.web.Widget.setElement` with whatever it
generated or the widget's behavior is undefined.r
generated or the widget's behavior is undefined.
.. note::