好的,我的设置有些复杂,但是我得到的错误仍然很有趣。
我有一个用于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的函数?还是我错过了什么?
答案 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;
}