【问题标题】:Class instance cannot be passed to function containing Jasmine describe- and it-blocks类实例不能传递给包含 Jasmine describe- 和 it-blocks 的函数
【发布时间】:2019-07-02 17:27:38
【问题描述】:

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

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

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"
    }
}

所有测试都通过,除了 passObject 函数内的 it 块检查对象(已传递的类实例)是否未定义。

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 块内调用passObject(myFunction, myObjectInstance);,但这并不意味着beforeAll 被执行。除非我弄错了这应该被视为一个正常的函数调用,所以它只会在没有与其他测试相同的上下文的情况下运行,即 beforeAll 执行和初始化 myObjectInstance = new MyObject(); 编辑:实际上,我是在这里有点愚蠢 - obj 只被使用一次,那是在 beforeAll 之前执行 passObject(myFunction, myObjectInstance); 时 - 那时没有为变量分配任何东西。
  • 是的,Jasmine 似乎执行了该函数,因此在执行 beforeAll()s 和 it()s 之前,在其上下文中将 undefined 传递为 myObjectInstance。如果我将对passObject 的调用包装在describe() 块中,它也会这样做。
  • 我查看了一些 Jasmine 文档,但找不到一个好的 Jasmine 方式来做你想做的事。我会建议您改用“普通 JS”解决方案。我将在答案中概述这一点。如果有更熟悉 Jasmine 的人出现,他们可能会在这里有更合适的解决方案。

标签: javascript jasmine


【解决方案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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-17
    • 1970-01-01
    • 2014-04-15
    相关资源
    最近更新 更多