前端angular基础测试篇
<p> </p><style>.title { text-align: center; margin-bottom: 0.2em }
.subtitle { text-align: center; font-size: medium; font-weight: bold; margin-top: 0 }
.todo { font-family: monospace; color: rgba(255, 0, 0, 1) }
.done { font-family: monospace; color: rgba(0, 128, 0, 1) }
.priority { font-family: monospace; color: rgba(255, 165, 0, 1) }
.tag { background-color: rgba(238, 238, 238, 1); font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal }
.timestamp { color: rgba(190, 190, 190, 1) }
.timestamp-kwd { color: rgba(95, 158, 160, 1) }
.org-right { margin-left: auto; margin-right: 0; text-align: right }
.org-left { margin-left: 0; margin-right: auto; text-align: left }
.org-center { margin-left: auto; margin-right: auto; text-align: center }
.underline { text-decoration: underline }
#postamble p, #preamble p { font-size: 90%; margin: 0.2em }
p.verse { margin-left: 3% }
pre { border: 1px solid rgba(204, 204, 204, 1); box-shadow: 3px 3px 3px rgba(238, 238, 238, 1); padding: 8pt; font-family: monospace; overflow: auto; margin: 1.2em }
pre.src { position: relative; overflow: visible; padding-top: 1.2em }
pre.src:before { display: none; position: absolute; background-color: rgba(255, 255, 255, 1); top: -10px; right: 10px; padding: 3px; border: 1px solid rgba(0, 0, 0, 1) }
pre.src:hover:before { display: inline }
pre.src-asymptote:before { content: "Asymptote" }
pre.src-awk:before { content: "Awk" }
pre.src-C:before { content: "C" }
pre.src-clojure:before { content: "Clojure" }
pre.src-css:before { content: "CSS" }
pre.src-D:before { content: "D" }
pre.src-ditaa:before { content: "ditaa" }
pre.src-dot:before { content: "Graphviz" }
pre.src-calc:before { content: "Emacs Calc" }
pre.src-emacs-lisp:before { content: "Emacs Lisp" }
pre.src-fortran:before { content: "Fortran" }
pre.src-gnuplot:before { content: "gnuplot" }
pre.src-haskell:before { content: "Haskell" }
pre.src-hledger:before { content: "hledger" }
pre.src-java:before { content: "Java" }
pre.src-js:before { content: "Javascript" }
pre.src-latex:before { content: "LaTeX" }
pre.src-ledger:before { content: "Ledger" }
pre.src-lisp:before { content: "Lisp" }
pre.src-lilypond:before { content: "Lilypond" }
pre.src-lua:before { content: "Lua" }
pre.src-matlab:before { content: "MATLAB" }
pre.src-mscgen:before { content: "Mscgen" }
pre.src-ocaml:before { content: "Objective Caml" }
pre.src-octave:before { content: "Octave" }
pre.src-org:before { content: "Org mode" }
pre.src-oz:before { content: "OZ" }
pre.src-plantuml:before { content: "Plantuml" }
pre.src-processing:before { content: "Processing.js" }
pre.src-python:before { content: "Python" }
pre.src-R:before { content: "R" }
pre.src-ruby:before { content: "Ruby" }
pre.src-sass:before { content: "Sass" }
pre.src-scheme:before { content: "Scheme" }
pre.src-screen:before { content: "Gnu Screen" }
pre.src-sed:before { content: "Sed" }
pre.src-sh:before { content: "shell" }
pre.src-sql:before { content: "SQL" }
pre.src-sqlite:before { content: "SQLite" }
pre.src-forth:before { content: "Forth" }
pre.src-io:before { content: "IO" }
pre.src-J:before { content: "J" }
pre.src-makefile:before { content: "Makefile" }
pre.src-maxima:before { content: "Maxima" }
pre.src-perl:before { content: "Perl" }
pre.src-picolisp:before { content: "Pico Lisp" }
pre.src-scala:before { content: "Scala" }
pre.src-shell:before { content: "Shell Script" }
pre.src-ebnf2ps:before { content: "ebfn2ps" }
pre.src-cpp:before { content: "C++" }
pre.src-abc:before { content: "ABC" }
pre.src-coq:before { content: "Coq" }
pre.src-groovy:before { content: "Groovy" }
pre.src-bash:before { content: "bash" }
pre.src-csh:before { content: "csh" }
pre.src-ash:before { content: "ash" }
pre.src-dash:before { content: "dash" }
pre.src-ksh:before { content: "ksh" }
pre.src-mksh:before { content: "mksh" }
pre.src-posh:before { content: "posh" }
pre.src-ada:before { content: "Ada" }
pre.src-asm:before { content: "Assembler" }
pre.src-caml:before { content: "Caml" }
pre.src-delphi:before { content: "Delphi" }
pre.src-html:before { content: "HTML" }
pre.src-idl:before { content: "IDL" }
pre.src-mercury:before { content: "Mercury" }
pre.src-metapost:before { content: "MetaPost" }
pre.src-modula-2:before { content: "Modula-2" }
pre.src-pascal:before { content: "Pascal" }
pre.src-ps:before { content: "PostScript" }
pre.src-prolog:before { content: "Prolog" }
pre.src-simula:before { content: "Simula" }
pre.src-tcl:before { content: "tcl" }
pre.src-tex:before { content: "TeX" }
pre.src-plain-tex:before { content: "Plain TeX" }
pre.src-verilog:before { content: "Verilog" }
pre.src-vhdl:before { content: "VHDL" }
pre.src-xml:before { content: "XML" }
pre.src-nxml:before { content: "XML" }
pre.src-conf:before { content: "Configuration File" }
table { border-collapse: collapse }
caption.t-above { caption-side: top }
caption.t-bottom { caption-side: bottom }
td, th { vertical-align: top }
th.org-right { text-align: center }
th.org-left { text-align: center }
th.org-center { text-align: center }
td.org-right { text-align: right }
td.org-left { text-align: left }
td.org-center { text-align: center }
dt { font-weight: bold }
.footpara { display: inline }
.footdef { margin-bottom: 1em }
.figure { padding: 1em }
.figure p { text-align: center }
.equation-container { display: table; text-align: center; width: 100% }
.equation { vertical-align: middle }
.equation-label { display: table-cell; text-align: right; vertical-align: middle }
.inlinetask { padding: 10px; border: 2px solid rgba(128, 128, 128, 1); margin: 10px; background: rgba(255, 255, 204, 1) }
#org-div-home-and-up { text-align: right; font-size: 70%; white-space: nowrap }
textarea { overflow-x: auto }
.linenr { font-size: smaller }
.code-highlighted { background-color: rgba(255, 255, 0, 1) }
.org-info-js_info-navigation { border-style: none }
#org-info-js_console-label { font-size: 10px; font-weight: bold; white-space: nowrap }
.org-info-js_search-highlight { background-color: rgba(255, 255, 0, 1); color: rgba(0, 0, 0, 1); font-weight: bold }
.org-svg { width: 90% }</style>
<div id="content">
<div id="table-of-contents">
<h2>目录</h2>
<div id="text-table-of-contents">
<ul>
<li>1. 常用jest断言</li>
<li>2. mock函数
<ul>
<li>2.1. 函数的调用捕捉</li>
<li>2.2. 使用spyOn间谍测试服务</li>
<li>2.3. angular TestBed</li>
</ul>
</li>
</ul>
</div>
</div>
<p>jest提供覆盖率报告等开发者所需要的所有测试工具,jest是一款几乎零配置的 测试框架。angular jest单元测试的写法为三步,引入测试内容,运行测试内容, 最后进行比较,是否达到预期。jest中的断言使用expect, 它接受一个参数,就 是运行测试内容的结果,返回一个对象,这个对象来调用匹配器 (toBe) ,匹配器的参数就是我们的预期结果,这样就可以对结果和预 期进行对比了,也就可以判断对不对了。</p>
<p>两个必会的方法test方法:Jest封装的测试方法,一般填写两个参数,描述和测 试方法,expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得 到的预期是什么。</p>
<div id="outline-container-org46171c0" class="outline-2">
<h2 id="org46171c0"><span class="section-number-2">1</span> 常用jest断言</h2>
<div id="text-1" class="outline-text-2">
<ul class="org-ul">
<li>toBe:绝对相等(<code>=</code>)</li>
</ul>
<pre class="example">// 在src/functions.js中创建被测试的模块
export default {
sum(a, b) {
return a + b;
}
}
// 在test/functions.test.js文件中创建测试用例
import functionsfrom '../src/functions';
test('sum(2 + 2) 等于 4', () => {
expect(functions.sum(2, 2)).toBe(4);
})
</pre>
<ul class="org-ul">
<li>not:允许你测试结果不等于某个值的情况</li>
</ul>
<pre class="example">// 在src/functions.js中创建被测试的模块
export default {
sum(a, b) {
return a + b;
}
}
// functions.test.js
import functionsfrom '../src/functions'
test('sum(2, 2) 不等于 5', () => {
expect(functions.sum(2, 2)).not.toBe(5);
})
</pre>
<ul class="org-ul">
<li>toEqual:简单类型绝对匹配;复杂类型内容结果的匹配</li>
</ul>
<pre class="example">// functions.js
export default {
getAuthor() {
return {
name: 'LITANGHUI',
age: 24,
}
}
}
// functions.test.js
import functionsfrom '../src/functions';
test('getAuthor()返回的对象深度相等', () => {
expect(functions.getAuthor()).toEqual(functions.getAuthor());
})
test('getAuthor()返回的对象内存地址不同', () => {
expect(functions.getAuthor()).not.toBe(functions.getAuthor());
})
</pre>
<ul class="org-ul">
<li>toHaveLength:方便的用来测试字符串和数组类型的长度是否满足预期</li>
</ul>
<pre class="example">// functions.js
export default {
getIntArray(num) {
if (!Number.isInteger(num)) {
throw Error('"getIntArray"只接受整数类型的参数');
}
let result = [];
for (let i = 0, len = num; i < len; i++) {
result.push(i);
}
return result;
}
}
// functions.test.js
import functionsfrom '../src/functions';
test('getIntArray(3)返回的数组长度应该为3', () => {
expect(functions.getIntArray(3)).toHaveLength(3);
})
</pre>
<ul class="org-ul">
<li>toThorw:可能够让我们测试被测试方法是否按照预期抛出异常,但是在使用时 需要注意的是:我们必须使用一个函数将将被测试的函数做一个包装,正如上 面getIntArrayWrapFn所做的那样,否则会因为函数抛出导致该断言失败</li>
</ul>
<pre class="example">// functions.test.js
import functionsfrom '../src/functions';
test('getIntArray(3.3)应该抛出错误', () => {
function getIntArrayWrapFn() {
functions.getIntArray(3.3);
}
expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整数类型的参数');
mockImplementation})
</pre>
<ul class="org-ul">
<li>toBeNull():匹配null</li>
<li>toBeUndefined():匹配undefined</li>
<li>toBeDefined():匹配非undefined</li>
<li>toBeTruthy():匹配转化后为true</li>
<li>toBeFalsy():匹配转化后为false</li>
<li>toBeGreaterThan():相当于大于号</li>
<li>toBeLessThan():相当于小于号</li>
<li>toBeGreaterThanOrEqual():相当于大于等于号</li>
<li>toBeLessThanOrEqual():相当于小于等于号</li>
<li>toBeCloseTo():解决js浮点错误</li>
<li>toMatch(regExp/string):用正则表达式或者字符串匹配字符串片段</li>
<li>toContain():匹配数组或者Set中的某一项</li>
</ul>
</div>
</div>
<div id="outline-container-org677dbc4" class="outline-2">
<h2 id="org677dbc4"><span class="section-number-2">2</span> mock函数</h2>
<div id="text-2" class="outline-text-2">
<p>进行单元测试时,要测试的内容依赖其他内容,比如异步请求,会依赖网络,很 可能造成测试达不到效果。能不能把依赖变成可控的内容?这就用到mack。mack 就是把依赖替换成我们可控的内容,实现测试的内容和它的依赖项隔离。那怎么 才能实现mock呢?使用mack 函数。在jest中,当我们谈论mack的时候,其实谈 论的就是使用mack函数代替依赖。mack函数就是一个虚拟的或假的函数,所以对 它来说,最重要的就是实现依赖的全部功能,从而起到替换的作用。通常,mock 函数会提供以下三个功能,来实现替换:函数的调用捕获,设置函数返回值,改 变原函数的实现。在jest 创建一个mock 函数最简单的方法就是调用jest.fn() 方法。</p>
</div>
<div id="outline-container-org4b9667e" class="outline-3">
<h3 id="org4b9667e"><span class="section-number-3">2.1</span> 函数的调用捕捉</h3>
<div id="text-2-1" class="outline-text-3">
<p>捕获调用指的是这个函数有没有被调用,调用的参数是什么,返回值是什么,通 常用于测试回调函数,模拟真实的回调函数。</p>
<pre class="example">// functions.js
export default {
forEachFun: (array: any[], callback: Function) => {
array.forEach((i) => callback(i));
}
}
// functions.test.js
import functionsfrom '../src/functions';
test('forEachFun调用每个方法', () => {
const mockFun = jest.fn();
const testArr = ;
functions.forEachFun(testArr, mockFun);
console.log(mockFun.mock);
// expect(mockFun.mock.calls.length).toBe(2);
expect(mockFun).toHaveBeenCalled();
expect(mockFun).toBeCalledTimes(2);
expect(mockFun).toBeCalledWith(1);
expect(mockFun).toBeCalledWith(2);
});
// mock函数mockFun属性是一个对象,打印结果:
{
calls: [ [ 1 ], [ 2 ] ],
instances: [ undefined, undefined ],
invocationCallOrder: [ 1, 2 ],
results:[
{ type: 'return', value: undefined },
{ type: 'return', value: undefined }
]
}
</pre>
<p>calls 保存的就是调用状态。calls 是一个数组,每一次的调用都组成数组的 一个元素,在这里调用了两次,就有两个元素。每一个元素又是一个数组,它 则表示的是函数调用时的参数,因为每次的调用都传递了一个参数给函数,所 以数组只有一项。如果有多个参数,数组就有多项,按照函数中的参数列表依 次排列。这时候,就可以做断言,函数调用了几次,就判断 calls.length. expect(mockFun.mock.calls.length).toBe(2) 就是断言函数 是不是调用了两次。expcet(mockFun.mock.calls) .toBe(1)就是断言第 一次调用的时候传递的参数是不是1. 可能觉得麻烦了, 的确有点麻烦了,幸 好,jest 对函数的mock参数进行了简单的封装,提供了简单的匹配器。</p>
<pre class="example">// 用来判断mock函数是否被掉用过
toHaveBeenCalled()/toBeCalled()
// 用来判断mock函数调用过几次
toHaveBeenCalledTimes(number)/toBeCalledTimes(number)
// 用来判断是否使用了特定参数调mock函数
toHaveBeenCalledWith(arg1,arg2,...)/toBeCalledWith(arg1,arg2,...)
</pre>
<p>有的时候,由于后端没有开发好,或网络问题,不想调用函数,直接获取到函数 的返回值就可以了,比如异步函数, 直接返回一个Observable就好了,根本没有 必要请求服务器。</p>
<pre class="example">test('设置函数返回值', () => {
// 普通返回
const mockFun = jest.fn();
mockFun.
const result: string = mockFun();
expect(mockFun).toBeCalledTimes(1);
expect(result).toEqual({name: 'shao'});
// observable返回
const observableFun = jest.fn();
observableFun.mockReturnValue(of({name: 'shao'}));
const observableResult: Observable<any> = observableFun();
observableResult.subscribe((res) => {
expect(res).toEqual({name: 'shao'});
});
});
</pre>
</div>
</div>
<div id="outline-container-orgc325317" class="outline-3">
<h3 id="orgc325317"><span class="section-number-3">2.2</span> 使用spyOn间谍测试服务</h3>
<div id="text-2-2" class="outline-text-3">
<p>服务往往是最容易进行单元测试的文件。下面是一些针对 ValueService 的同步 和异步单元测试,甚至不需要 Angular 测试工具的帮助。</p>
<pre class="example">// Straight Jasmine testing without Angular's testing support
describe('ValueService', () => {
let service: ValueService;
beforeEach(() => { service = new ValueService(); });
it('#getValue should return real value', () => {
expect(service.getValue()).toBe('real value');
});
it('#getObservableValue should return value from observable',
() => {
service.getObservableValue().subscribe(value => {
expect(value).toBe('observable value');
});
});
});
</pre>
<p>服务通常依赖于angular 在构造函数中注入的其它服务。在很多情况下,调用 服务的构造函数时,很容易手动创建和注入这些依赖。</p>
<pre class="example">// ValueService.ts
@Injectable({
providedIn: 'root'
})
export class ValueService {
value = 'real value';
getValue(): string { return this.value; }
setValue(value: string): void { this.value = value; }
getObservableValue(): Observable<string> { return of('observable value'); }
getPromiseValue(): Promise<string> { return Promise.resolve('promise value'); }
getObservableDelayValue(): Observable<string> {
return of('observable delay value').pipe(delay(10));
}
}
// ValueService.spec.ts
export class FakeValueService extends ValueService {
value = 'faked service value';
}
describe('MasterService without angular testing support', () => {
let masterService: MasterService;
it('#getValue 返回 service', () => {
masterService = new MasterService(new ValueService());
expect(masterService.getValue()).toBe('real value');
});
it('#getValue 使用 fakeServiece', () => {
masterService = new MasterService(new FakeValueService());
expect(masterService.getValue()).toBe('faked service value');
});
it('#getValue 使用 fake object', () => {
const fake = { getValue: () => 'fake value' };
masterService = new MasterService(fake as ValueService);
expect(masterService.getValue()).toBe('fake value');
});
it('#getValue 返回 by a spy', () => {
const valueService: ValueService = new ValueService();
jest.spyOn(valueService, 'getValue').mockReturnValueOnce('test');
masterService = new MasterService(valueService);
expect(masterService.getValue()).toBe('test');
});
});
</pre>
<p>第一个测试使用 new 创建了一个 ValueService,并把它传给了 MasterService 的构造函数。然而,注入真实服务很难工作良好,因为大多数被依赖的服务都很 难创建和控制。还可以模拟依赖、使用仿制品,或者在相关的服务方法上创建一 个测试间谍spyOn。</p>
</div>
</div>
<div id="outline-container-orgae00b69" class="outline-3">
<h3 id="orgae00b69"><span class="section-number-3">2.3</span> angular TestBed</h3>
<div id="text-2-3" class="outline-text-3">
<p>TestBed 是 Angular 测试实用工具中最重要的。 TestBed 创建了一个动态构造 的 Angular 测试模块,用来模拟一个 Angular 的 @NgModule 。 TestBed.configureTestingModule() 方法接受一个元数据对象,它可以拥有 @NgModule的大部分属性。要测试某个服务,你可以在元数据属性 providers 中 设置一个要测试或模拟的服务数组。</p>
<pre class="example">let masterService: MasterService;
let valueService: ValueService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MasterService, ValueService
]
});
masterService = TestBed.inject(MasterService);
valueService = TestBed.inject(ValueService);
});
</pre>
<p>测试带依赖的服务时,同时使用spyOn:</p>
<pre class="example">// value.service.ts
@Injectable()
export class MasterService {
constructor(private valueService: ValueService) { }
getValue(): string {
return this.valueService.getValue();
}
}
// value.service.spec.ts
describe('MasterService动态构建', () => {
let masterService: MasterService;
let valueService: ValueService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MasterService, ValueService
]
});
masterService = TestBed.inject(MasterService);
valueService = TestBed.inject(ValueService);
});
it('#getValue从spy返回', () => {
const stubValue = 'stub value';
jest.spyOn(valueService, 'getValue').mockReturnValue(stubValue);
expect(masterService.getValue()).toBe(stubValue);
expect(valueService.getValue).toBeCalledTimes(1);
window.console.log(valueService.getValue);
expect(valueService.getValue()).toBe(stubValue);
});
});
</pre>
</div>
</div>
</div>
</div>
<div id="postamble" class="status">
<p class="author">Author: flysic</p>
<p class="date">Created: 2021-10-24 Sun 22:26</p>
<p class="validation">Validate</p>
</div><br><br>
来源:https://www.cnblogs.com/machine/p/15456062.html
頁:
[1]