Get help with testing, discuss unit testing strategies etc.


Post by klodoma »

I have the following running code.
I use methods of some custom classes that execute the cases. All good.

I would like to know if there is a short-hand for the chain method. The following code:
  t.it('Test Delete trigger:event', function (t) {
                t.chain(
                    function (next) {
                        me.resetViewData(t, next);
                    },
                    function (next) {
                        me.waitForListMode(t, next);
                    },
                    { click: me.actionTargets.deleteClick },
                    { waitForMs: 10 },
                    function (next) {
                        me.view.fireEvent('callDelete');
                        next();
                    },
                    function (next) {
                        me.runDelete(t, next);
                    },
                    function (next) {
                        me.waitForListMode(t, next);
                    },
                    function (next) {
                        me.checkDeleteResult(t, next);
                    }
                );
            });
could look like:
            t.it('Test Delete trigger:event', function (t) {
                t.chain(
                    { method: me.resetViewData, scope: me, args: [t] }, //next should be automatically passed as last argument
                    { method: me.waitForListMode, scope: me, args: [t] },
                    { method: me.resetViewData, scope: me, args: [t] },
                    { method: me.resetViewData, scope: me, args: [t] },
                    { waitForMs: 10 },
                    function (next) {
                        me.view.fireEvent('callDelete');
                        next();
                    },
                    { method: me.runDelete, scope: me, args: [t] },
                    { method: me.waitForListMode, scope: me, args: [t] },
                    { method: me.checkDeleteResult, scope: me, args: [t] }
                );
            });
Is this possible?

Thanks in advance!

Post by nickolay »


Post by klodoma »

I tried that, but the scope(this in the function) is not correct if I use that.

Post by mats »

Docs don't mention any 'scope', so cannot expect that to work. Just bind your methods instead? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Post by klodoma »

mats wrote: Mon Sep 09, 2019 12:32 pm Docs don't mention any 'scope', so cannot expect that to work. Just bind your methods instead? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
Yes, this was my alternative in case the scope cannot be set.

Maybe it could be a feature request, something like this:
            
            t.chain(
                { scope: me, defaultArgs: [t]},
                { method: me.method1},
                { method: me.method2},
                { method: me.method3},
            );
My main reasoning for this is is that in the tests one has to write quite some chain methods in order to achieve some behavior and these methods can be stored in some re-usable test-classes.

Here, I put a use-case that I would definitely use:
    
    
//test-case    
let testHelper = new TestHelper({
    panel: panel,
    list: list
});

t.it("Item Edit Test", function(t){
    t.chain(
        { scope: testHelper, defaultArgs: [t] },
        //select the item
        { method: testHelper.selectItem, args: [item] }, //t, next are passed automatically (defaultArgs + next)
        //check that the panel is in read mode
        { method: testHelper.checkPanelReadMode },
        //hit edit
        { click: 'button[Edit]' },
        //check that edit mode is enabled
        { method: testHelper.checkPanelEditMode }
        ...
    );
}
    
//this is my testHelper
class testHelper {
    
    ...
    
    selectItem: function (selector, t, next) {
        const me = this;
        t.chain(
            { waitFor: 'ComponentVisible', me.list },
            { dblclick: selector },
            { waitForEvent: [me.list, 'item-selected'] },
            next
        );
    },

    checkPanelReadMode: function (t, next) {
        const me = this;
        t.pass('checkPanelStatus');
        t.is(me.panel.getHeader().isVisible(), true, 'Header visible');
        t.is(me.getEditorEl().isDisabled(), true, 'Editor should be disabled');
        //do some other checks - for read mode
        next();
    },

    checkPanelEditMode: function (t, next) {
        const me = this;
        t.pass('checkPanelStatus');
        t.is(me.panel.getHeader().isVisible(), true, 'Header visible');
        t.is(me.panel.getEditorEl().isDisabled(), false, 'Editor should be enabled');
        //do some other checks - for edit mode
        next();
    },

    ...
}
PS: I know I can achieve this with the current setup with binding and passing t as argument

Post by nickolay »

The scope is always assumed to be an instance of the current test. It can be generalized to arbitrary function call as you suggest, but in that case "scope" will be required for every step (it won't be saved as an internal state of the `t.chain` call - thats too specific).

The test class is the intended place for the helper methods.

Also, the whole concept of chaining is obsolete with modern JS - one should just go for async/await:
t.it('Some test', async t => {
    await t.click('target')

    const res   = await someHelper.method(arg1, arg2)

    if (res === 1) {
        await t.click('target')
    } else {
        for (let i = 0; i < 11; i++) {
            await t.click('target' + i)
        }
    }
    
    // etc etc
})

Post by klodoma »

nickolay wrote: Mon Sep 09, 2019 5:14 pm Also, the whole concept of chaining is obsolete with modern JS - one should just go for async/await:
Yeah, this is a very good point. Ok, give it a thought :) till then I'll use the binding.

Thanks for the feedback!

Post Reply