单元测试 angular 4 指令具有 @input 属性

Unit test angular 4 directive having @input property

我有一个应用于输入类型文本框的指令 AllowOnlyNumbers

<input                         
                    [AllowOnlyNumbers]=true
                    [maxLength]= 'yearlyFieldMaxLength'
                    type="tel"
                    name="totalAnnualIncome"
                    formControlName="totalAnnualIncome"
                    [(ngModel)]="yearlyIncomeAmt"
                    (focus)="onFocusEnableToolTip('TotalAnnualIncome')" 
                    (focusout)="onFocusOutDisableToolTip('TotalAnnualIncome')" 
                     minlength="2"
                     autocomplete="off"/>

一个非常简单的指令限制用户只能在文本框中输入数字。

import { Directive, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[AllowOnlyNumbers]'
})

/**
 * @method AllowOnlyNumbers
 * @desc This directive restricts the keyboard entry to numbers only.
 * Users can enter numbers and can use backspace, tab,enter, escape, end, home, left, right and del keys.
 * Usage: <input type = "text" [AllowOnlyNumbers] = true />
 */

export class AllowOnlyNumbers {

  constructor() { }

  @Input() AllowOnlyNumbers: boolean;
  /**
   * @methodspace AllowOnlyNumbers
   * @method onKeyDown
   * @desc It restricts the keyboard entry to numbers only.
   * @argument event
   * @returns returns only digit
   *
   */
  @HostListener('keydown', ['$event']) onKeyDown(event) {
    const e = event as KeyboardEvent;
    if (this.AllowOnlyNumbers) {
      // Allow: 8=Backspace, 9= Tab, 13=CR, 27=ESC, 35=END, 36=HOME, 37=Left, 39=Right, 46=DEL
      if ([8, 9, 13, 27, 35, 36, 37, 39, 46].indexOf(e.keyCode) !== -1) {
        return;
      }

      // Ensure that it is a number and stop the keypress
      if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
        e.preventDefault();
      }
    }
  }
}

现在,在使用 jasmine 编写单元测试用例时,我无法将 @Input() AllowOnlyNumbers 属性 设置为 true。这始终是未定义的。 下面是我的 UT 文件。 (注意:Keydown 事件被触发)

import {ComponentFixture, TestBed} from '@angular/core/testing';
import { AllowOnlyNumbers } from './allow-only-numbers.directive';
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component({
  template: `<input [AllowOnlyNumbers]= true type="text" name="totalAnnualIncome"  />`
})
// tslint:disable-next-line:no-unnecessary-class
class TestAllowOnlyNumbersComponent {
 //  allowNumbers = true;
}
fdescribe('Directive: AllowOnlyNumbers', () => {
  let component: TestAllowOnlyNumbersComponent;
  let fixture: ComponentFixture<TestAllowOnlyNumbersComponent>;
  let inputEl: DebugElement;
  let linkDes;
  let routerLinks;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [TestAllowOnlyNumbersComponent, AllowOnlyNumbers],
      schemas:      [ NO_ERRORS_SCHEMA ]
    });
    fixture = TestBed.createComponent(TestAllowOnlyNumbersComponent);
    component = fixture.componentInstance;
    inputEl = fixture.debugElement.query(By.css('input[name="totalAnnualIncome"]'));
  });

  it('keydown input', () => {
    inputEl.triggerEventHandler('keydown', {});
    fixture.detectChanges();
    expect(true).toBe(true);
  });

});

我正在使用 this link 作为参考。我无法将 @Input() AllowOnlyNumbers 属性 设置为 true。这始终是未定义的。

您问题的回答:

TestAllowOnlyNumbersComponent中应该是[AllowOnlyNumbers]="true"而不是[AllowOnlyNumbers]= true

你实际上在做的是[AllowOnlyNumbers]=,它没有为指令的输入分配任何值。

此外,您应该将 fixture.detectChanges() 移动到 triggerEventHandler 之前以触发初始值绑定,或者更好的做法是将其添加到 beforeEach

的末尾
  beforeEach(() => {
    ...
    fixture.detectChanges();
  });

  it('keydown input', () => {
    inputEl.triggerEventHandler('keydown', {});
    expect(true).toBe(true);
  });

还有关于您的指令的附加评论:

我认为您应该在指令中将 keyCode 替换为 key,因为看起来 keyCode 已被弃用 https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent。 我认为更改应该很容易,在您的指令中,您只需读取该键字符串和基于该值的代码 const code = e.key.charCodeAt()

然后我会创建以下测试,在这种情况下测试其中一个键 'F' key:

  it('keydown input', () => {
    const event = new KeyboardEvent('keydown', { key: 'F' });
    inputEl.nativeElement.dispatchEvent(event);
    expect(event.defaultPrevented).toBe(true);
  });

我想这在指令中进行更改时可能会起作用。