Writing tests for client side code

The more code you have on the client side, the more it becomes a liability. For server side code, there's the well-entrenched unit tests, and for JavaScript, we have QUnit (https://qunitjs.com), which Odoo uses.

Getting ready

We'll add our tests to the addon developed in the previous recipes, so grab the code from the recipe Making RPC calls to the server and put it in a new module called ch15_r04.

How to do it...

We have to add our test file and make it known to the test mechanism in the appropriate template.

  1. Add the static/test/ch15_r04.js file:
    odoo.define_section('ch15_r04', ['ch15_r04'], function (test, mock) {
        test('FieldMany2OneButtons', function(assert, ch15_r04)
        {
  2. Create a minimal implementation of FieldManager:
            var fake_field_manager = {
                get_field_desc: function()
                {
                    return {
                        'relation': 'res.users',
                        'domain': [],
                    };
                },
                on: function() {},
                off: function() {},
                get: function() {},
                $el: jQuery(),
            },
  3. Instantiate our widget:
            widget = new ch15_r04.FieldMany2OneButtons(
                fake_field_manager,
                {
                    attrs: {
                        modifiers: '{}',
                        name: 'field_name',
                        widget: 'many2one_buttons',
                    },
                }
            ),
  4. Create a container to add our widget to:
            $container = jQuery('<div/>'),
  5. Tell the test framework we're testing an asynchronous action:
            async_result = assert.async();
  6. Add data to simulate our RPC call:
            mock.add(
                '/web/dataset/search_read', function()
                {
                    return {
                        records: [
                            {
                                id: 1,
                                display_name: 'Administrator',
                            },
                            {
                                id: 4,
                                display_name: 'Demo user',
                            },
                        ],
                        length: 2,
                    }
                }
            );
  7. Do the actual testing:
            widget.attachTo($container)
            .then(function()
            {
                widget.renderElement();
                assert.deepEqual(
                    widget.$el.find('button').map(function()
                    {
                        return jQuery.trim(jQuery(this).text())
                    }).get(),
                    ['Administrator', 'Demo user'],
                    'Check if the widget shows the users we expect'
                );
                async_result();
            });
        })
    });
  8. Make the test file known to the test mechanism in views/templates.xml:
        <template id="qunit_suite" inherit_id="web.qunit_suite">
            <xpath expr="//head" position="inside">
                <script src="/ch15_r04/static/test/ch15_r04.js" type="text/javascript" />
            </xpath>
        </template>

When you navigate to /web/tests now, the web client will run all tests available. You should find our test in this list too, hopefully with a positive result. Given a lot of tests will run there, it can be simpler to also pass the module you want to test. In our case, the path would be /web/tests?module=ch15_r04.

Note

Note that what you pass as the module parameter in the test URL is not necessarily the name of your addon, but whatever section name you define in your define_section call above.

How it works...

You define your JavaScript tests in a very similar manner to how you would make normal JavaScript code known to Odoo: You call a function that gets your code as a parameter. In the case of tests, this function is called define_section and expects a logical name for the tests to be run, the JavaScript packages the test requires (in our case, we just need the code our module provides, so we pass ['ch15_r04'] here), and a function that sets up the tests themselves.

This function is provided with a function called test and an object called mock. The test function is what we call to actually declare tests; it receives a name for the single test and the function that contains the test code. This function will be provided with an assert object and all the packages we required in the call to define_section as parameters.

Within this last function, the actual testing happens. We need to set up a couple of helper objects because a widget expects to live on a form, which in turn implements an interface called FieldManager. For simplicity, we provide a minimal implementation of that in order not to have to pull a whole form as dependency for our test. With this, we can instantiate our widget and also pass it an idealized version of a parsed XML node that would be the field's definition in the form view.

At the end of the function, we can attach our widget to the container element we created, call the renderElement function, and check if rendering caused the DOM elements to show up as we expected.

For doing those checks, the assert object has a few functions like equal, deepEqual, notEqual and notDeepEqual, which all deal with equality, but the deep* variants recurse into compound types like arrays and objects. Then there's ok and notOk which can be used to assert true or false value and throws, to assert a function, throws a certain exception or error message.

There's more...

The example code contains two peculiarities which makes this test more complicated than others; it needs to request data from the server, and as a consequence of that, run asynchronously. Both pose challenges to the test framework.

For fetching data, Odoo offers an object called mock, which we can fill with data that will be returned when code requests data from the server. So what we do here is assign a JavaScript function per server URL our test code is expected to query and have the function return what we consider the expected result. Then the test is if the client side code reacts to this data appropriately. If your test code makes calls to different models or functions, or to the same but with different parameters, this function would have to react to those parameters. But as we only read data once, it suffices to add a handler for /web/dataset/search_read, and we can just can return a fixed result. If your own code involves more interactions with the server, you'll probably need a smarter handler that actually looks at its parameters and returns different results for different requests.

Then, because of the asynchronous call, we need to tell the test framework that our function exiting is not yet the end of the test by calling assert.async(). This returns a function we are supposed to call exactly once within our asynchronous handler in order to notify the test framework that this specific test is done.

See also

Unfortunately, at the time of writing, many of the more complex JavaScript community modules are not yet migrated to Odoo 9.0, so there are not many examples of client side tests in the wild. Consult the web module's tests for some examples: https://github.com/OCA/OCB/tree/9.0/addons/web/static/test.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.124.143