让我们想象一下相当简单的茉莉花单元测试设置
let actions$: Subject<Action>;
let effects: CustomEffects;
beforeEach(() => {
actions$ = new Subject<Action>();
TestBed.configureTestingModule({
providers: [
CustomEffects,
provideMockActions(() => actions$),
],
});
effects = TestBed.inject(CustomEffects);
});
testLoadingIndicators(
simpleParamsPassedWithoutProblem,
{ effects, actions$ }, // <-- undefined, undefined
);
我想创建可重用的测试函数(在其他文件中定义),它将采用一些参数、效果和 actions$ 变量。问题是最后两个是在 beforeEach 中初始化的,所以当它们传递给我的测试 fn 时,它们还没有准备好。
我的最终目标是拥有可以在测试设置中的许多文件中重复使用的测试功能。此测试函数具有
describes
和 its
并使用提供的参数来执行逻辑。
编写此类测试的通常方法使用闭包来访问变量,但这意味着在外部描述中声明我的测试 fn ,从而使其不可共享。
不要使用范围来共享数据。
beforeEach()
和 beforeAll()
已经可以使用 this
在所有测试之间设置可共享数据:设置函数中定义的任何属性都将在 it()
回调中可见。在
describe()
中无法完成同样的操作,但是
beforeEach()
可以在
describe()
回调中调用,并且作用域仅限于该回调:
beforeAll(function() {
this.foo = 1;
});
beforeEach(function() {
this.bar = 2;
});
describe('beforeAll', function() {
it('this.foo is 1', function() {
expect(this.foo).toEqual(1);
});
});
describe('beforeEach', function() {
it('this.bar is 2', function() {
expect(this.bar).toEqual(2);
});
});
describe('`this` is passed down to `it()` calls not to describe()', function() {
this.bar = 3;
this.baz = "whatever";
it('this.foo is inherited', function() {
expect(this.foo).toEqual(1);
});
it('this.bar is NOT overridden in describe()', function() {
expect(this.bar).toEqual(2);
});
it('this.baz has not been added in describe()', function() {
expect(this.baz).toBeUndefined();
});
const thisInDescribe = this;
it('describe() does not have access to the `this` in `beforeEach()`/`beforeAll()', function() {
expect(this).not.toEqual(thisInDescribe);
});
});
describe('can use beforeEach() in describe ', function() {
beforeEach(function() {
this.bar = 3;
this.baz = 4;
});
it('this.foo is inherited', function() {
expect(this.foo).toEqual(1);
});
it('this.bar is overridden in describe()', function() {
expect(this.bar).toEqual(3);
});
it('this.baz has not been added in describe()', function() {
expect(this.baz).toEqual(4);
});
});
describe('beforeEach() used in a describe() does not leak to other describe()s ', function() {
it('this.bar is still the same as the global beforeEach()', function() {
expect(this.bar).toEqual(2);
});
it('this.baz does not exist', function() {
expect(this.baz).toBeUndefined();
});
});
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine-html.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot0.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot1.min.js"></script>
重要提示1:您必须使用常规函数而不是箭头函数,才能正确使用this
,因为箭头函数在创建时已设置
this
。欲了解更多信息,请参阅
重要提示 2: beforeAll()
设置应该很少使用,因为它可能会污染测试环境。最好使用
beforeEach()
这将保证在测试之间重新设置上下文。如果需要,可以使用拆卸功能afterAll()
/
afterEach()
在测试后进行清理。但这很少需要。有了这个机制,您就可以提取
describe()
和/或
it()
:
//this can be in another file
function parametarisedDescribe(whatToAdd, whatToExpect) {
describe('this is some test', function() {
it('adding is correct', function() {
const actual = this.bar + whatToAdd;
expect(actual).toEqual(whatToExpect);
});
});
}
function parametariseIt(whatToRepeat, whatToExpect) {
it('repeating is correct', function() {
const actual = whatToRepeat.repeat(this.bar);
expect(actual).toEqual(whatToExpect);
});
}
//only in test file
beforeEach(function() {
this.bar = 2;
});
parametarisedDescribe(40, 42); // 2 + 40 = 42 - pass
parametarisedDescribe(40, 41); // 2 + 40 != 41 - fail
describe("static describe() with parametarised it()s", function() {
parametariseIt("a", "aa"); // "a".repeat(2) = "aa" - pass
parametariseIt("b", "BB"); // "b".repeat(2) != "BB" - fail
});
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine-html.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot0.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot1.min.js"></script>
请注意,也可以仅提取给
describe()
/
it()
的回调并使用它们。例如:
//define describe()/it() callback separately
function myDescribe() {
it('adding is correct', function() {
const actual = 2 + this.whatToAdd;
expect(actual).toEqual(this.whatToExpect);
});
}
function myIt() {
const actual = this.whatToRepeat.repeat(2);
expect(actual).toEqual(this.whatToExpect);
}
//use the callbacks
//NOTE: these are all in here for convenience
// any group of these tests can also be in a separate spec file
describe("adding test passes", function() {
beforeEach(function() {
this.whatToAdd = 40;
this.whatToExpect = 42;
});
describe('2 + 40 == 42', myDescribe);
});
describe("addint test fails", function() {
beforeEach(function() {
this.whatToAdd = 40;
this.whatToExpect = 41;
});
describe('2 + 40 != 41', myDescribe);
});
describe('repeating "a" ', function() {
beforeEach(function() {
this.whatToRepeat = "a";
this.whatToExpect = "aa";
});
it('"a".repeat(2) == "aa"', myIt);
});
describe('repeating "b"', function() {
beforeEach(function() {
this.whatToRepeat = "b";
this.whatToExpect = "BB";
});
it('"b".repeat(2) != "BB"', myIt);
});
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/jasmine-html.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot0.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/5.0.1/boot1.min.js"></script>