Jasmine을 사용한 Angular 단위 테스트 소개

青灯夜游
풀어 주다: 2020-08-26 10:19:20
앞으로
2742명이 탐색했습니다.
<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> 1. 개념 </strong></span></p> <p><span style="font-size: 18px;"><strong>Test Suite</strong></span></p> <p>Test Suite는 단순한 클래스라도 여러 개의 테스트 케이스를 갖기 때문에 이러한 테스트 케이스를 하나의 카테고리에 모아 놓은 것을 <strong>Test Suite</strong>라고 합니다. </p> <p>Jasmine에서는 <code>describe</code> 전역 함수로 표현되는데 첫 번째 문자열 매개변수는 Suite의 이름이나 제목을 나타내는 데 사용되고 두 번째 메서드 매개변수는 Suite 코드를 구현하는 데 사용됩니다. <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><div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div></p><p><span style="font-size: 20px;">Specs<strong></strong></span></p>A Specs는 테스트를 위해 구현하는 특정 코드 본문인 테스트 사례와 동일합니다. <p><span style="font-size: 18px;">Jasmine은 <code>describe</code>와 유사하게 <code>it</code> 전역 함수를 문자열과 메소드라는 두 개의 매개변수와 함께 사용하여 표현합니다. <strong></strong>각 사양에는 테스트해야 하는 코드를 테스트하기 위한 여러 가지 기대 사항이 포함되어 있습니다. 기대 사항의 결과가 <code>false</code>인 경우에는 테스트 사례가 실패한 상태임을 의미합니다. </span><div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div></p>🎜🎜Expectations🎜🎜🎜🎜<code>expect</code> 전역 함수를 사용하여 표현된 Assertions는 테스트할 🎜실제 값🎜의 대표만 수신하고, 🎜기대값🎜. 🎜🎜🎜🎜 2. 일반적으로 사용되는 방법🎜🎜🎜🎜🎜🎜Matchers🎜🎜🎜🎜매칭 연산을 수행하고, 실제 값과 예상 값을 비교하고, 결과를 Jasmine에 통보합니다. 마지막으로 이 Spec의 성공 여부를 Jasmine이 판단합니다. 아니면 실패합니다. 🎜🎜Jasmine은 매우 풍부한 API를 제공하며 일반적으로 사용되는 일부 일치 프로그램을 제공합니다. 🎜<ul><li><code>toBe()</code>는 <code>===</code></li><li와 동일합니다. >toNotBe ()는 <code>!==</code>와 동일합니다.</li><li>toBeDefined()는 <code>!== 정의되지 않음</code></li><li>toBeUndefine( )는 <code>= == 정의되지 않음</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()는 함수가 호출되었는지 확인합니다.</li><li>toHaveBeenCalledWith()는 들어오는 매개변수가 매개변수로 호출되었는지 확인합니다.</li><li>toMatch()는 동일합니다. <code> new RegExp().test()</code></li><li>toNotMatch()는 <code>!new RegExp().test()</code></li><와 동일합니다. li>toThrow() 함수에서 오류가 발생하는지 확인</li></ul>🎜이 API는 이전에 음수 값의 판단을 나타내기 위해 <code>not</code>을 사용했습니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div>🎜이러한 Matcher는 일상적인 요구 사항을 거의 충족할 수 있습니다. 물론 특별한 요구 사항에 맞게 자신만의 Matcher를 맞춤 설정할 수도 있습니다. 🎜🎜🎜🎜설정 및 해제🎜🎜🎜🎜가능한 테스트 코드는 매우 중요하므로 이러한 반복되는 설정 및 해제 코드를 내부의 해당 <code>beforeEach</code> 및 <code >afterEach</code>에 넣을 수 있습니다. 글로벌 기능. 🎜🎜<code>beforeEach</code>는 각 사양이 실행되기 전을 의미하며 그 반대의 경우도 마찬가지입니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div>🎜🎜🎜데이터 공유🎜🎜🎜🎜위의 예에서와 같이 각 테스트 파일 <code>describe</code>의 시작 부분에 해당 변수를 정의할 수 있습니다. > 내부적으로 공유할 수 있습니다. 🎜🎜물론 각 Spec 실행 주기에는 Spec 실행 후 지워질 때까지 빈 <code>this</code> 객체가 수반됩니다. <code>this</code를 사용하여 데이터 공유도 수행할 수 있습니다. > . 🎜🎜🎜🎜중첩된 코드🎜🎜🎜🎜가끔 컴포넌트를 테스트할 때 컴포넌트의 상태가 달라서 다른 결과를 표시하는 경우가 있습니다. 이때 <code>describe</code> 하나만 사용하면 너무 우아해 보일 수 있습니다. . 🎜🎜따라서 <code>describe</code>를 중첩하면 테스트 코드와 테스트 보고서가 더욱 아름답게 보입니다. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><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><div class="contentsignin">로그인 후 복사</div></div>🎜🎜🎜테스트 코드 블록 건너뛰기🎜🎜🎜🎜요구는 언제나 반성인데 최종 작성된 테스트 코드는 삭제해야 할까요? 아니요...🎜🎜Suites 및 Specs는 각각 <code>xdescribe</code> 및 <code>xit</code> 전역 함수를 사용하여 이러한 테스트 코드 블록을 건너뛸 수 있습니다. 🎜🎜🎜🎜3. Angular 도구 세트와 협력🎜🎜🎜🎜🎜🎜Spy🎜🎜🎜<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;"><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><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;"><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><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;"><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><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;"><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><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;"><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><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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!