登录  /  注册
首页 > web前端 > js教程 > 正文

使用Jasmine进行Angular单元测试的方法介绍

青灯夜游
发布: 2020-08-22 11:23:26
转载
2498人浏览过
<p>本篇文章给大家谈谈如何使用jasmine进行angular单元测试?有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。</p> <p><img src="https://img.php.cn/upload/article/000/000/024/5f408f91953d2985.jpg" alt="使用Jasmine进行Angular单元测试的方法介绍" ></p> <p>以下是我假定那些极少或压根没写单元测试的人准备的,因此,会白话解释诸多概念性问题,同时会结合 Jasmine 与之对应的方法进行讲解。</p> <p><span style="font-size: 20px;"><strong>一、概念</strong></span></p> <p><span style="font-size: 18px;"><strong>Test Suite</strong></span></p> <p>测试套件,哪怕一个简单的类,也会有若干的测试用例,因此将这些测试用例集合在一个分类下就叫<strong>Test Suite</strong>。</p> <p>而在 Jasmine 就是使用 <code>describe</code> 全局函数来表示,它的第一个字符串参数用来表示Suite的名称或标题,第二个方法参数就是实现Suite代码了。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">describe(&#39;test suite name&#39;, () => { });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 18px;"><strong>Specs</strong></span></p><p>一个Specs相当于一个测试用例,也就是我们实现测试具体代码体。</p><p>Jasmine 就是使用 <code>it</code> 全局函数来表示,和 <code>describe</code> 类似,字符串和方法两个参数。</p><p>而每个 Spec 内包括多个 expectation 来测试需要测试的代码,只要任何一个 expectation 结果为 <code>false</code> 就表示该测试用例为失败状态。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">describe(&#39;demo test&#39;, () => { const VALUE = true; it(&#39;should be true&#39;, () => { expect(VALUE).toBe(VALUE); }) });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 18px;"><strong>Expectations</strong></span></p><p>断言,使用 <code>expect</code> 全局函数来表示,只接收一个代表要测试的<strong>实际值</strong>,并且需要与 Matcher 代表<strong>期望值</strong>。</p><p><span style="font-size: 20px;"><strong>二、常用方法</strong></span></p><p><span style="font-size: 18px;"><strong>Matchers</strong></span></p><p>断言匹配操作,在实际值与期望值之间进行比较,并将结果通知Jasmine,最终Jasmine会判断此 Spec 成功还是失败。</p><p>Jasmine 提供非常丰富的API,一些常用的Matchers:</p><ul><li><code>toBe()</code> 等同 <code>===</code></li><li>toNotBe() 等同 <code>!==</code></li><li>toBeDefined() 等同 <code>!== undefined</code></li><li>toBeUndefined() 等同 <code>=== undefined</code></li><li>toBeNull() 等同 <code>=== null</code></li><li>toBeTruthy() 等同 <code>!!obj</code></li><li>toBeFalsy() 等同 <code>!obj</code></li><li>toBeLessThan() 等同 <code><</code></li><li>toBeGreaterThan() 等同 <code>></code></li><li>toEqual() 相当于 <code>==</code></li><li>toNotEqual() 相当于 <code>!=</code></li><li>toContain() 相当于 <code>indexOf</code></li><li>toBeCloseTo() 数值比较时定义精度,先四舍五入后再比较。</li><li>toHaveBeenCalled() 检查function是否被调用过</li><li>toHaveBeenCalledWith() 检查传入参数是否被作为参数调用过</li><li>toMatch() 等同 <code>new RegExp().test()</code></li><li>toNotMatch() 等同 <code>!new RegExp().test()</code></li><li>toThrow() 检查function是否会抛出一个错误</li></ul><p>而这些API之前用 <code>not</code> 来表示负值的判断。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">expect(true).not.toBe(false);</pre><div class="contentsignin">登录后复制</div></div><p>这些Matchers几乎可以满足我们日常需求,当然你也可以定制自己的Matcher来实现特殊需求。</p><p><span style="font-size: 18px;"><strong>Setup 与 Teardown</strong></span></p><p>一份干将的测试代码很重要,因此我们可以将这些重复的 setup 与 teardown 代码,放在与之相对应的 <code>beforeEach</code> 与 <code>afterEach</code> 全局函数里面。</p><p><code>beforeEach</code> 表示每个 Spec 执行之前,反之。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">describe(&#39;demo test&#39;, () => { let val: number = 0; beforeEach(() => { val = 1; }); it(&#39;should be true&#39;, () => { expect(val).toBe(1); }); it(&#39;should be false&#39;, () => { expect(val).not.toBe(0); }); });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 18px;"><strong>数据共享</strong></span></p><p>如同上面示例中,我们可以在每个测试文件开头、<code>describe</code> 来定义相应的变量,这样每个 <code>it</code> 内部可以共享它们。</p><p>当然,每个 Spec 的执行周期间也会伴随着一个空的 <code>this</code> 对象,直至 Spec 执行结束后被清空,利用 <code>this</code> 也可以做数据共享。</p><p><span style="font-size: 18px;"><strong>嵌套代码</strong></span></p><p>有时候当我们对某个组件进行测试时,而这个组件会有不同状态来展示不同的结果,这个时候如果只用一个 <code>describe</code> 会显得不过优雅。</p><p>因此,嵌套 <code>describe</code>,会让测试代码、测试报告看起来更漂亮。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">describe(&#39;AppComponent&#39;, () => { describe(&#39;Show User&#39;, () => { it(&#39;should be show panel.&#39;, () => {}); it(&#39;should be show avatar.&#39;, () => {}); }); describe(&#39;Hidden User&#39;, () => { it(&#39;should be hidden panel.&#39;, () => {}); }); });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 18px;"><strong>跳过测试代码块</strong></span></p><p>需求总是三心二意的,但好不容易写好的测试代码,难道要删除吗?非也……</p><p>Suites 和 Specs 分别可以用 <code>xdescribe</code> 和 <code>xit</code> 全局函数来跳过这些测试代码块。</p><p><span style="font-size: 20px;"><strong>三、配合Angular工具集</strong></span></p><p><span style="font-size: 18px;"><strong>Spy</strong></span></p><p><a href="https://img.php.cn/upload/article/000/000/024/5f45c5bc2de7d900.jpg" target="_blank">Angular</a>的自定义事件实在太普遍了,但为了测试这些自定义事件,因此监控事件是否正常被调用是非常重要。好在,<code>Spy</code> 可以用于监测函数是否被调用,这简直就是我们的好伙伴。</p><p>以下示例暂时无须理会,暂且体验一下:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">describe(&#39;AppComponent&#39;, () => { let fixture: ComponentFixture<TestComponent>; let context: TestComponent; beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent] }); fixture = TestBed.createComponent(TestComponent); context = fixture.componentInstance; // 监听onSelected方法 spyOn(context, &#39;onSelected&#39;); fixture.detectChanges(); }); it(&#39;should be called [selected] event.&#39;, () => { // 触发selected操作 // 断言是否被调用过 expect(context.onSelected).toHaveBeenCalled(); }); });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 18px;"><strong>异步支持</strong></span></p><p>首先,这里的异步是指带有 Observable 或 Promise 的异步行为,因此对于组件在调用某个 Service 来异步获取数据时的测试状态。</p><p>假设我们的待测试组件代码:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">export class AppComponent { constructor(private _user: UserService) {} query() { this._user.quer().subscribe(() => {}); } }</pre><div class="contentsignin">登录后复制</div></div><p><strong>async</strong></p><p><code>async</code> 无任何参数与返回值,所有包裹代码块里的测试代码,可以通过调用 <code>whenStable()</code> 让<strong>所有待处理异步行为都完成后</strong>再进行回调;最后,再进行断言操作。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">it(&#39;should be get user list (async)&#39;, async(() => { // call component.query(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(true).toBe(true); }); }));</pre><div class="contentsignin">登录后复制</div></div><p><strong>fakeAsync</strong></p><p>如果说 <code>async</code> 还需要回调才能进行断点让你受不了的话,那么 <code>fakeAsync</code> 可以解决这一点。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">it(&#39;should be get user list (async)&#39;, fakeAsync(() => { // call component.query(); tick(); fixture.detectChanges(); expect(true).toBe(true); }));</pre><div class="contentsignin">登录后复制</div></div><p>这里只是将回调换成 <code>tick()</code>,怎么样,是不是很酷。</p><p><strong>Jasmine自带异步</strong></p><p>如前面所说的异步是指带有 Observable 或 Promise 的异步行为,而有时候我们有些东西是依赖 <code>setTimeout</code> 或者可能是需要外部订阅结果以后才能触发时怎么办呢?</p><p>可以使用 <code>done()</code> 方法。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">it(&#39;async demo&#39;, (done: () => void) => { context.show().subscribe(res => { expect(true).toBe(true); done(); }); el.querySelected(&#39;xxx&#39;).click(); });</pre><div class="contentsignin">登录后复制</div></div><p><span style="font-size: 20px;"><strong>四、结论</strong></span></p> <p>本章几乎所有的内容在Angular单元测试经常使用到的东西;特别是异步部分,三种不同异步方式并非共存的,而是需要根据具体业务而采用。否则,你会发现真TM难写单元测试。毕竟这是一个异步的世界。</p> <p>自此,我们算是为Angular写单元测试打下了基础。后续,将不会再对这类基础进行解释。</p> <p>happy coding!</p> <p>相关教程推荐:<a href="https://www.php.cn/course/list/20.html" target="_blank">angular教程</a></p>

以上就是使用Jasmine进行Angular单元测试的方法介绍的详细内容,更多请关注php中文网其它相关文章!

智能AI问答
PHP中文网智能助手能迅速回答你的编程问题,提供实时的代码和解决方案,帮助你解决各种难题。不仅如此,它还能提供编程资源和学习指导,帮助你快速提升编程技能。无论你是初学者还是专业人士,AI智能助手都能成为你的可靠助手,助力你在编程领域取得更大的成就。
来源:segmentfault网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
最新问题
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2024 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号