测试驱动开发(Test Driven Development, TDD)和行为驱动开发(Behavior Driven Development, BDD)
Jasmine 通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法, 行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的
被测系统(System under test, SUT)
单测不负责检查跨类或者跨系统的交互逻辑,那都是集成测试的范围
单测不能受到外界环境的影响, 依赖需要用本地实现注入,或者提供一个mock(桩对象)
单测需要能快速执行,有必要在每次修改代码时运行单测
单测应随编码进行,补单测是没有意义的
1 | ├───lib |
spec + src文件夹是栗子
笔记目的导向:
- spec.ts写的啥(Jasmine 单元测试的书写语法)
- 组件声明周期中应该存在哪些用例(如实例化,输入/输出,方法,释放)
- 依赖项的处理init with Node.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58describe("Player", function() {
var player;
var song;
beforeEach(function() {
player = new Player();
song = new Song();
});
it("should be able to play a Song", function() {
player.play(song);
expect(player.currentlyPlayingSong).toEqual(song);
//demonstrates use of custom matcher
expect(player).toBePlaying(song);
});
describe("when song has been paused", function() {
beforeEach(function() {
player.play(song);
player.pause();
});
it("should indicate that the song is currently paused", function() {
expect(player.isPlaying).toBeFalsy();
// demonstrates use of 'not' with a custom matcher
expect(player).not.toBePlaying(song);
});
it("should be possible to resume", function() {
player.resume();
expect(player.isPlaying).toBeTruthy();
expect(player.currentlyPlayingSong).toEqual(song);
});
});
// demonstrates use of spies to intercept and test method calls
it("tells the current song if the user has made it a favorite", function() {
spyOn(song, 'persistFavoriteStatus');
player.play(song);
player.makeFavorite();
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
});
//demonstrates use of expected exceptions
describe("#resume", function() {
it("should throw an exception if song is already playing", function() {
player.play(song);
expect(function() {
player.resume();
}).toThrowError("song is already playing");
});
});
});1
2npm install --save-dev jasmine
npx jasmine initdescribe
以describe分组specs,它代表一组相似的测试用例,通常有 2 个参数:字符串和方法。字符串作为特定 Suite 的名字和标题。方法是包含实现的代码。1
2
3
4
5
6describe ('HeroesService (with spies)', () => {
...
});
describe('HeroesService (with mocks)', () => {
...
});waitForAsync TestBed
1
2
3
4
5
6
7
8
9
10
11
12
13
14import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
describe('nz-table', () => {
let injector: Injector;
beforeEach(
waitForAsync(() => { //
injector = TestBed.configureTestingModule({
imports: [BidiModule, NzTableModule],
declarations: [NzTestTableBasicComponent, NzTestTableScrollComponent, NzTableSpecCrashComponent, NzTestTableRtlComponent]
});
TestBed.compileComponents();
})
);
...
})it 和 specs
specs即specification(规则),它们是一个个断言,可以是 true 或者 false。当每个 Spec 中的所有 expectations 都是 true,则通过测试。以it函数定义,与describe类似的,有 2 个参数:标题和方法。expect tobe
1
2expect(true).toBe(true);
expect(false).not.toBe(true);断言
- toBe 和 toEqual 前者相当于比较运算符=== 后者比较字面量的值(对于对象进行属性的比较)
- toMatch
- toBeDefined 和 toBeNull
- toContain
- toBeGreaterThan 和 toBeLessThan
- toBeCloseTo
- toThrow
beforeEach和afterEach
分别在每个it断言测试前/后调用spy
存根(stub)和跟踪(track)任意函数spy一个foo对象上的setBar方法,分别断言该方法被调用、被调用若干次、被以某某参数调用1
2
3
4
5
6
7
8
9
10
11
12
13
14spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
it("tracks that the spy was called x times", function() {
expect(foo.setBar).toHaveBeenCalledTimes(2);
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
很多时候用spy对应模拟对象