首页 > 解决方案 > TypeError:创建单元测试时无法读取 null 的属性“addEventListener” - Jasmine(Angular)

问题描述

我对 Jasmine 进行了单元测试,我得到的是这个

TypeError: Cannot read property 'addEventListener' of null

实际代码是这样的...

  ngAfterViewInit() {

    this.autoCompleteInput = <HTMLInputElement>document.querySelector('.search-input');

    this.autoCompleteInput.addEventListener('blur', this.onBlur.bind(this));
    this.autoCompleteInput.addEventListener('input', this.onInput.bind(this));
    this.autoCompleteInput.addEventListener('focus', this.onFocus.bind(this));

    this.renderer.setAttribute(this.inputRef.nativeElement, 'aria-autocomplete', 'both');
    if (this.filter !== undefined && this.filter !== null && this.filter !== '') {
      this.filter = '';
    }

  }

addEventListener 的第一行有效

this.autoCompleteInput.addEventListener('blur', this.onBlur.bind(this));

但其余的因ng test --code-coverage --watch=false而失败

当我检查报告时,我看到:

在此处输入图像描述

我不明白为什么 onBlur 测试很好,而其他的却不行?

这是我的茉莉花代码:

    import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
    import { RouterTestingModule } from '@angular/router/testing';
    import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
    import { AutocompleteComponent } from './autocomplete.component';
    import { filter } from 'lodash';

    describe('Auto Complete Component', () => {
        let autoCompleteComponent: AutocompleteComponent;
        let fixture: ComponentFixture<AutocompleteComponent>;
        let autoCompleteInput: HTMLInputElement;
        let filteredItems: string[] = [];
        let $window, $provide, listeners;
        // let rendered: DebugElement;

        beforeEach(async(() => {
            TestBed.configureTestingModule({
                imports: [
                    RouterTestingModule.withRoutes([])
                ],
                declarations: [
                    AutocompleteComponent
                ],
                providers: [AutocompleteComponent],
                schemas: [CUSTOM_ELEMENTS_SCHEMA]
            }).compileComponents().then(() => {
                fixture = TestBed.createComponent(AutocompleteComponent);
                autoCompleteComponent = fixture.componentInstance;
                fixture.detectChanges();
            });

        }));

        it('should create call ngOnChanges ', () => {
            jasmine.createSpy('ngOnChanges').and.callThrough();
            autoCompleteComponent.ngOnChanges();
            expect(autoCompleteComponent.ngOnChanges()).toHaveBeenCalled();
        });

        it('should create filterItems() Function ', () => {
            jasmine.createSpy('filterItems').and.callThrough();
            expect(autoCompleteComponent.filterItems).toBeUndefined();
        });

        it('should create call clearFocus ', () => {
            jasmine.createSpy('clearFocus').and.callThrough();
            autoCompleteComponent.clearFocus();
            expect(autoCompleteComponent.clearFocus()).toHaveBeenCalled();
        });

        it('should call onBlur Event ', () => {
            jasmine.createSpy('onBlur').and.callThrough();
            autoCompleteComponent.onBlur(event);
            expect(autoCompleteComponent.onBlur(event)).toHaveBeenCalled();
        });

        it('should call onItemSelect Event ', () => {

            let item = '';

            jasmine.createSpy('onItemSelect').and.callThrough();
            jasmine.createSpy('clearFocus').and.callThrough();

            expect(autoCompleteComponent.onItemSelect(event, item)).toHaveBeenCalled();
            expect(autoCompleteComponent.itemSelect.emit(item)).toHaveBeenCalled();
            expect(autoCompleteComponent.clearFocus()).toHaveBeenCalled();

        });

        it('should call onFocus Event ', () => {
            jasmine.createSpy('onFocus').and.callThrough();
            autoCompleteComponent.onFocus(event);
            expect(autoCompleteComponent.autoCompleteInput.focus()).toHaveBeenCalled;
            // expect(autoCompleteComponent.onFocus(event)).toHaveBeenCalled();
        });

        it('should call onInput Event ', () => {

            jasmine.createSpy('onInput').and.callThrough();
            autoCompleteComponent.onInput(event);
            expect(autoCompleteComponent.onInput(event)).toHaveBeenCalled();
        });

    });

标签: karma-jasmine

解决方案


经过大量实验,我找到了答案,我希望这对其他人有所帮助。

这是解决方案:

ngAfterViewInit() {

    this.autoCompleteInput = <HTMLInputElement>document.querySelector('.search-input');

    console.log('Add Event Listener: ', this.autoCompleteInput);

    this.bindOnBlurStateEventCallback();
    this.bindOnInputStateEventCallback();
    this.bindOnFocusStateEventCallback();

    this.renderer.setAttribute(this.autoCompleteInput, 'aria-autocomplete', 'both');
    if (this.filter !== undefined && this.filter !== null && this.filter !== '') {
      this.filter = '';
    }

  }

  public bindOnBlurStateEventCallback(): void {

    this.autoCompleteInput.addEventListener('blur', this.onBlur.bind(this));
    document.querySelector('.search-input').addEventListener('blur', () => {
      console.log('You selected: ', this.autoCompleteInput.value);
    });
  }
  
  public bindOnInputStateEventCallback(): void {
    this.autoCompleteInput.addEventListener('input', this.onInput.bind(this));
  }
  
  public bindOnFocusStateEventCallback(): void {
    this.autoCompleteInput.addEventListener('focus', this.onFocus.bind(this));
  }

在 spec.ts 文件中:

    it('adds listener events', function () {
        spyOn(document, 'addEventListener').and.callThrough();
        spyOn(window, 'addEventListener').and.callThrough();

        expect(document.addEventListener.prototype).not.toBeTruthy;
        expect(window.addEventListener.prototype).not.toBeTruthy;

        expect(document.addEventListener.prototype).toBeTruthy;
        expect(window.addEventListener.prototype).toBeTruthy;
    });

你有它!


推荐阅读