无法将类实例传递给包含Jasmine describe-和it-blocks的函数

时间:2019-07-02 08:31:40

标签: javascript jasmine

好的,我的设置有些复杂,但是我得到的错误仍然很有趣。

我有一个用于webdriverio testrunner的Jasmine测试套件,其中包含在其他地方声明的函数,我将一个类的实例传递给该函数。该功能又包含更多嵌套的describe-和it-blocks。

const MyObject = require('./my-object');
const { passObject, myFunction } = require('./obj-fns');

var myObjectInstance;

describe("testing passing objects as parameters", function() {
    beforeAll(function() {
        myObjectInstance = new MyObject();
    });

    it("for a class instance inside describe/it block directly", function() {
        browser.pause(250);
        expect(myObjectInstance).not.toBe(undefined);
        expect(myObjectInstance.selector).toBe("object-class-instance");
    });

    it("for a function inside describe/it block directly", function() {
        browser.pause(250);
        expect(myFunction).not.toBe(undefined);
        expect(myFunction().selector).toBe("object-function");
    });

    passObject(myFunction, myObjectInstance);

});

my-object.js

class MyObject {
    constructor() {
        this.selector = "object-class-instance";
    }
}

module.exports = MyObject;

obj-fns.js

module.exports.passObject = function(fn, obj) {
    describe("inside a function that has a describe block", function() {

        it("and an it block for a class instance", function() {
            browser.pause(250);
            expect(obj).not.toBe(undefined);
            expect(obj.selector).toBe("object-class-instance");
        });

        it("and an it block for a function", function() {
            browser.pause(250);
            expect(fn).not.toBe(undefined);
            expect(fn().selector).toBe("object-function");
        })

    });
}

module.exports.myFunction = function() {
    return {
        selector: "object-function"
    }
}

it函数内的passObject块以外的所有测试均通过,该块检查对象(通过的类实例)是否未定义。

pass: testing passing objects as parameters for a class instance inside describe/it block directly
pass: testing passing objects as parameters for a function inside describe/it block directly
pass: testing passing objects as parameters inside a function that has a describe block and an it block for a function

fail: testing passing objects as parameters inside a function that has a describe block and an it block for a class instance (chrome_undefinedVersion with 0-0 runner)
Error: Expected undefined not to be undefined.
    at <Jasmine>
    at UserContext.<anonymous> (C:\dev\eclipse-workspace\FrontendTesting\Daimler.Van.SPP.Dashboard.Web.FrontendTesting\spec\obj-fns.js:6:29)
    at <Jasmine> 

这是设计的吗?如果是的话,如何将类实例传递给具有describe-和it-blocks的函数?还是我错过了什么?

1 个答案:

答案 0 :(得分:1)

问题在于,当您调用passObject(myFunction, myObjectInstance);变量而为其赋值之前,立即调用myObjectInstance 。因此,passObject中的代码运行时,每次测试都将obj用作undefined,因为这是当时传递的。

您可以改为执行其他操作,然后将工厂功能传递给测试功能。这样一来,您就可以在主要测试和passObject中的测试之间共享代码,同时仍然可以灵活地使用其他对象运行passObject

这是代码的样子:

module.exports.passObject = function(fn, createObj) {
    describe("inside a function that has a describe block", function() {
        beforeAll(function() {
            this.obj = createObj();
        });

        it("and an it block for a class instance", function() {
            browser.pause(250);
            expect(this.obj).not.toBe(undefined);
            expect(this.obj.selector).toBe("object-class-instance");
        });

        it("and an it block for a function", function() {
            browser.pause(250);
            expect(fn).not.toBe(undefined);
            expect(fn().selector).toBe("object-function");
        })

    });
}

passObject现在采用创建obj的函数,而不仅仅是采用obj。它还使用this上下文将该对象传递给测试。

使用此方法时,您需要提取对象的初始化并在两个地方重复使用:

function createInstance { return new MyObject(); }

describe("testing passing objects as parameters", function() {
    beforeAll(function() {
        myObjectInstance = createInstance()
    });

    it("for a class instance inside describe/it block directly", function() {
        browser.pause(250);
        expect(myObjectInstance).not.toBe(undefined);
        expect(myObjectInstance.selector).toBe("object-class-instance");
    });

    it("for a function inside describe/it block directly", function() {
        browser.pause(250);
        expect(myFunction).not.toBe(undefined);
        expect(myFunction().selector).toBe("object-function");
    });

    passObject(myFunction, createInstance);

});

这会导致轻微代码重复,因为在两种情况下beforeAll都会调用函数,将结果分配给变量,然后在测试中重新使用该变量。很好,因为在两种情况下变量都不同,所以不同的测试不必将其称为同一件事。但是,如果您要冒稍微混淆代码的风险,甚至可以消除这种重复。

首先,将整个beforeAll代码提取到

function setup() {
  this.instance = new MyObject();
}

然后您可以在beforeAll中直接使用

// in the main tests
beforeAll(setup)
module.exports.passObject = function(fn, setupFn) {
    describe("inside a function that has a describe block", function() {
        beforeAll(setupFn);
        /* ...tests...*/
   }
}

但是,这将意味着两个测试必须引用相同的内容:this.instance,并且尚不清楚要在this上下文中对测试进行初始化的内容。另外,一组测试可能需要初始化更多的东西,而另一组更少,例如,一个测试可能仅使用this.instance,而另一组测试也可能需要this.foo

因此,如果您选择这种方法,则必须保持测试范围,以免设置功能不断增长以适应每个可能的属性。

此外,如果您想为同一事物使用不同的名称,那么您仍然可以拥有两个指向相同值的属性。这将减轻两组测试所施加的某些样式。例如:

function setup() {
  //for the main tests
  this.myObjectInstance = new MyObject();

  //for `passObject` just alias the same thing:
  this.obj = this.myObjectInstance;
}