简体   繁体   English

Angular 单元测试指令

[英]Angular unit testing directive

I am trying to unit test my directive that looks like this我正在尝试对我的指令进行单元测试,看起来像这样

show-password.directive.ts显示密码.directive.ts

/* eslint-disable @typescript-eslint/no-explicit-any */
import { DOCUMENT } from '@angular/common';
import { Directive, ElementRef, Inject, OnInit } from '@angular/core';

@Directive({
    selector: '[securityPassword]'
})
export class ShowPasswordDirective implements OnInit {
    private _shown = false;
    private showIcon =
        // eslint-disable-next-line max-len
        '<img alt="" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIHZpZXdCb3g9IjAgMCAxNSAxNSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik03LjQ5OTA4IDMuNTYzMkM1LjE0NzU2IDMuNTIzNTQgMi43MTU1NCA1LjE2Njg2IDEuMTg2NTYgNi44NDk4NEMxLjAyNjA3IDcuMDI4MDIgMC45MzcyNTYgNy4yNTkzMiAwLjkzNzI1NiA3LjQ5OTEyQzAuOTM3MjU2IDcuNzM4OTIgMS4wMjYwNyA3Ljk3MDIyIDEuMTg2NTYgOC4xNDg0QzIuNjgyMjkgOS43OTU4IDUuMTA3MzEgMTEuNDc3IDcuNDk5MDggMTEuNDM2OEM5Ljg5MDg1IDExLjQ3NyAxMi4zMTY1IDkuNzk1OCAxMy44MTM0IDguMTQ4NEMxMy45NzM4IDcuOTcwMjIgMTQuMDYyNyA3LjczODkyIDE0LjA2MjcgNy40OTkxMkMxNC4wNjI3IDcuMjU5MzIgMTMuOTczOCA3LjAyODAyIDEzLjgxMzQgNi44NDk4NEMxMi4yODI2IDUuMTY2ODYgOS44NTA2IDMuNTIzNTQgNy40OTkwOCAzLjU2MzJaIiBzdHJva2U9IiMxRDFGMkEiIHN0cm9rZS13aWR0aD0iMS4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4NCjxwYXRoIGQ9Ik05LjY4NjcxIDcuNTAwNjhDOS42ODY2IDcuOTMzMzIgOS41NTgyIDguMzU2MjEgOS4zMTc3NSA4LjcxNTg4QzkuMDc3MzEgOS4wNzU1NSA4LjczNTYxIDkuMzU1ODUgOC4zMzU4NyA5LjUyMTMzQzcuOTM2MTMgOS42ODY4MiA3LjQ5NjMgOS43MzAwNiA3LjA3MTk4IDkuNjQ1NThDNi42NDc2NyA5LjU2MTExIDYuMjU3OTMgOS4zNTI3MSA1Ljk1MjA1IDkuMDQ2NzVDNS42NDYxNyA4Ljc0MDc5IDUuNDM3ODggOC4zNTA5OSA1LjM1MzUyIDcuOTI2NjZDNS4yNjkxNSA3LjUwMjMyIDUuMzEyNTEgNy4wNjI1IDUuNDc4MSA2LjY2MjhDNS42NDM2OSA2LjI2MzExIDUuOTI0MDggNS45MjE0OSA2LjI4MzgyIDUuNjgxMTRDNi42NDM1NSA1LjQ0MDc5IDcuMDY2NDggNS4zMTI1IDcuNDk5MTIgNS4zMTI1QzcuNzg2NDcgNS4zMTI0MiA4LjA3MTAyIDUuMzY4OTcgOC4zMzY1IDUuNDc4OTJDOC42MDE5OCA1LjU4ODg3IDguODQzMiA1Ljc1MDA1IDkuMDQ2MzYgNS45NTMyN0M5LjI0OTUyIDYuMTU2NDggOS40MTA2NCA2LjM5Nzc0IDkuNTIwNTEgNi42NjMyNUM5LjYzMDM5IDYuOTI4NzYgOS42ODY4NyA3LjIxMzMzIDkuNjg2NzEgNy41MDA2OFoiIHN0cm9rZT0iIzFEMUYyQSIgc3Ryb2tlLXdpZHRoPSIxLjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPg0KPC9zdmc+DQo=" />';
    private hideIcon = this.showIcon;
    private toWrap: any;
    private span: any;

    constructor(
        private el: ElementRef,
        @Inject(DOCUMENT) private document: Document
    ) {}

    ngOnInit(): void {
        this.toWrap = this.el.nativeElement;
        this.span = this.document.createElement('span');
        this.span.id = 't-toggle-type';
        this.span.innerHTML = this.showIcon;
        this.span.addEventListener('click', () => {
            this.toggle(this.span);
        });
        this.toWrap.parentNode.appendChild(this.span);
    }

    toggle(span: HTMLElement): void {
        this._shown = !this._shown;
        if (this._shown) {
            this.el.nativeElement.setAttribute('type', 'text');
            span.innerHTML = this.hideIcon;
        } else {
            this.el.nativeElement.setAttribute('type', 'password');
            span.innerHTML = this.showIcon;
        }
    }
}

Then my show-password.directive.spec.ts然后我的show-password.directive.spec.ts

import { DOCUMENT } from '@angular/common';
import {
    Component,
    CUSTOM_ELEMENTS_SCHEMA,
    DebugElement,
    ElementRef,
    NO_ERRORS_SCHEMA,
    Renderer2
} from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ShowPasswordDirective } from './show-password.directive';
import { MockElementRef, testingModule } from './utils/mocks';

@Component({
    template: `<input securityShowPassword type="password" id="t-password" />`
})
class TestSecurityShowPasswordDirectiveComponent {}

describe('TestSecurityShowPasswordDirectiveComponent ', () => {
    let fixture: ComponentFixture<TestSecurityShowPasswordDirectiveComponent>;
    let component: TestSecurityShowPasswordDirectiveComponent;
    // eslint-disable-next-line @typescript-eslint/no-unused-vars-experimental
    let de: DebugElement;

    beforeEach(() => {
        const imports = testingModule.imports();
        TestBed.configureTestingModule({
            imports: [imports],
            declarations: [
                TestSecurityShowPasswordDirectiveComponent,
                ShowPasswordDirective
            ],
            providers: [
                { provide: DOCUMENT, useValue: document },
                Renderer2,
                {
                    provide: ElementRef,
                    useClass: MockElementRef
                }
            ],
            schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(
            TestSecurityShowPasswordDirectiveComponent
        );
        component = fixture.componentInstance;
        de = fixture.debugElement;
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('should toggle type ', () => {
        // GIVEN
        const input = fixture.debugElement.query(By.css('#t-password'));
        const span = fixture.debugElement.query(By.css('#t-toggle-type'));
        const htmlElement: HTMLElement =
            fixture.nativeElement.querySelector('span');
        // WHEN
        span.nativeElement.click();
        // THEN
        expect(input).toBeTruthy();
    });
});

But my test got error但是我的测试出错了

TypeError: Cannot read properties of null (reading 'nativeElement')类型错误:无法读取 null 的属性(读取“nativeElement”)

It look like element span is not created, but in real testing all is working perfect, any info why span is not yet created??看起来元素 span 没有创建,但在实际测试中一切正常,任何信息为什么 span 尚未创建?

I am thinking remove { provide: ElementRef, useClass: MockElementRef } and remove { provide: DOCUMENT, useValue: document } .我正在考虑删除{ provide: ElementRef, useClass: MockElementRef }并删除{ provide: DOCUMENT, useValue: document } You most likely need the actual implementations of these dependencies for the span to be created.您很可能需要这些依赖项的实际实现才能创建span Also, call fixture.detectChanges() after component = fixture.componentInstance;另外,在component = fixture.componentInstance;之后调用fixture.detectChanges() ; for ngOnInit to be ran.运行ngOnInit

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM