具有 @input 属性的单元测试角度 4 指令

Posted

技术标签:

【中文标题】具有 @input 属性的单元测试角度 4 指令【英文标题】:Unit test angular 4 directive having @input property 【发布时间】:2018-09-04 15:12:45 【问题描述】:

我有一个指令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。这始终是未定义的。

【问题讨论】:

【参考方案1】:

回答您的问题:

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”键:

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

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

【讨论】:

对,你还应该在triggerEventHandler之前添加fixture.detectChanges();,这应该会触发初始值绑定,我认为应该可以。 是的,将 fixture.detectChanges() 向上移动,为我工作,您能否修改您的答案,以便我批准它。非常感谢。 完成了,很高兴我能帮上忙 :) 你能告诉我,我可以写什么来代替expect(true).toBe(true); 我已经用建议的更改和测试更新了我的答案

以上是关于具有 @input 属性的单元测试角度 4 指令的主要内容,如果未能解决你的问题,请参考以下文章

无法绑定到“(ngModel”,因为它不是角度单元测试用例中“输入”的已知属性

是否可以像我们在 Angular 2 中测试属性指令一样对结构指令进行单元测试

正则表达式测试作为属性的指令实现错误 - 验证(打字稿+角度)

在角度指令单元测试中注入服务

如何使用输入对 Angular 指令进行单元测试?

角度单元测试:TypeError:无法读取未定义的属性“根”