[WIP] testing in module guide
authorXavier Morel <xmo@openerp.com>
Mon, 18 Feb 2013 08:47:28 +0000 (09:47 +0100)
committerXavier Morel <xmo@openerp.com>
Mon, 18 Feb 2013 08:47:28 +0000 (09:47 +0100)
bzr revid: xmo@openerp.com-20130218084728-q92rcstsr2ca8pqa

addons/web/doc/module.rst
addons/web/doc/module/22 [new file with mode: 0644]
addons/web/doc/module/23 [new file with mode: 0644]
addons/web/doc/module/24 [new file with mode: 0644]
addons/web/doc/module/25 [new file with mode: 0644]
addons/web/doc/module/26 [new file with mode: 0644]
addons/web/doc/module/series
addons/web/doc/module/testing_0.png [new file with mode: 0644]
addons/web/doc/widget.rst

index 2e38733..138a8e9 100644 (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
diff --git a/addons/web/doc/module/22 b/addons/web/doc/module/22
new file mode 100644 (file)
index 0000000..5df76b3
--- /dev/null
@@ -0,0 +1,6 @@
+Index: web_example/static/src/tests/timer.js
+===================================================================
+--- /dev/null
++++ web_example/static/src/tests/timer.js
+@@ -0,0 +1 @@
++
diff --git a/addons/web/doc/module/23 b/addons/web/doc/module/23
new file mode 100644 (file)
index 0000000..d08a026
--- /dev/null
@@ -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");
++    });
++});
diff --git a/addons/web/doc/module/24 b/addons/web/doc/module/24
new file mode 100644 (file)
index 0000000..9dcf9e1
--- /dev/null
@@ -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'],
+ }
diff --git a/addons/web/doc/module/25 b/addons/web/doc/module/25
new file mode 100644 (file)
index 0000000..1d63dc7
--- /dev/null
@@ -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');
+     });
+ });
diff --git a/addons/web/doc/module/26 b/addons/web/doc/module/26
new file mode 100644 (file)
index 0000000..4085350
--- /dev/null
@@ -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");
++    });
+ });
index 6271f47..491e7dc 100644 (file)
@@ -17,3 +17,8 @@
 19
 20
 21
+22
+23
+24
+25
+26
diff --git a/addons/web/doc/module/testing_0.png b/addons/web/doc/module/testing_0.png
new file mode 100644 (file)
index 0000000..6271179
Binary files /dev/null and b/addons/web/doc/module/testing_0.png differ
index 483c6a6..7c4a237 100644 (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::