【问题标题】:Angular4 - how to unit test a directive using Karma and JasmineAngular4 - 如何使用 Karma 和 Jasmine 对指令进行单元测试
【发布时间】:2017-10-16 18:03:44
【问题描述】:

我创建了一个非常简单的指令用于输入元素,它应该只允许输入一个十进制数字(带有单个小数点的数字)。

指令定义如下:

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

// Directive attribute to stop any input, other than a decimal number.
@Directive({
    selector: '[decimalinput]'
})
export class DecimalInputDirective {

    constructor(private element : ElementRef) { }

    // Hook into the key press event.
    @HostListener('keypress', ['$event']) onkeypress( keyEvent : KeyboardEvent ) : boolean {

        // Check if a full stop already exists in the input.
        var alreadyHasFullStop = this.element.nativeElement.value.indexOf('.') != -1;

        // Get the key that was pressed in order to check it against the regEx.
        let input = String.fromCharCode(keyEvent.which);

        // Test for allowed character using regEx. Allowed is number or decimal.
        var isAllowed = /^(\d+)?([.]?\d{0,2})?$/.test( input );

        // If this is an invlid character (i.e. alpha or symbol) OR we already have a full stop, prevent key press.
        if (!isAllowed || (isAllowed && input == '.' && alreadyHasFullStop)){
            keyEvent.preventDefault();
            return false;
        }

        return true;
    }
}

该指令应允许"123.123" 而不是"abc""1.2.1"。现在我想测试这个指令,在线阅读,到目前为止我已经想出了这个:

import { Component, OnInit, TemplateRef,DebugElement, ComponentFactory, ViewChild, ViewContainerRef } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { DecimalInputDirective } from './decimalinput.directive';
import { By } from '@angular/platform-browser';

@Component({
    template: `<input type="text" name="txtDecimalTest" decimalinput>` 
})
class TestDecimalComponent { }


describe('Directive: DecimalInputDirective', () => {

    let component: TestDecimalComponent;
    let fixture: ComponentFixture<TestDecimalComponent>;
    let decimalInput: DebugElement;

    beforeEach(() => {

      TestBed.configureTestingModule({
        declarations: [TestDecimalComponent]
      });

      fixture = TestBed.createComponent(TestDecimalComponent);
      component = fixture.componentInstance;
      decimalInput = fixture.debugElement.query(By.css('input[name=txtDecimalTest]'));
    });

    it('Entering email and password emits loggedIn event', () => {

        // This sets the value (I can even put "abc" here and it will work.
        decimalInput.nativeElement.value = "12345";

        // But I am trying to initialize the keypress event, so the character is tested in a real world way when the user is using.
        decimalInput.nativeElement.dispatchEvent(new KeyboardEvent("keypress", { key: "a" })); // Nothing happens here!  This was my attempt...

        // This 
        expect(decimalInput.nativeElement.value).toBe("12345");
    });
});

从代码中可以看出,一行:

decimalInput.nativeElement.dispatchEvent(new KeyboardEvent...

我是在尝试模拟按键,就好像用户在输入一样。如果我模拟 a,然后是 b,然后是 c,然后是 1,然后是 2,然后是 3,我希望测试能够确保该值仅是“123”,并且在指令的工作方式中忽略了“abc”。

两个问题 - 1) 这是我应该做的正确测试吗? 2) 我的代码有什么问题 - 为什么模拟按键什么都不做?

提前感谢您的任何指点! :)

【问题讨论】:

  • 你是说如果用户输入123a,输入框会显示123?如果是这样,您希望将测试分解为更小的组件。如果没有,那么不要担心模拟按键。您无需测试按键是否有效,这是您在编写测试时可以做出的众多假设之一。
  • 是的,如果用户在哪里键入“abc123”,则只有“123”会作为允许的字符进入输入。我没有尝试测试按键功能,我尝试测试的是,如果使用指令在输入上按下“abc123”的模拟,那么实际上只有“123”在 input.value 中。这有意义吗?
  • 这看起来更像是端到端测试,您可以使用像 Selenium 这样的浏览器驱动程序来完成。设置输入值并分派更改事件以触发 Angular 接线足以进行单元测试。
  • @jonrsharpe 可能是对的,但我找到了this answer,如果您想忽略 jonrsharpe 的建议,您可能会发现它很有帮助。
  • 谢谢@jonrsharpe - 听起来我在单元测试这个指令时采取了错误的方法。如果我将我的字符测试代码提取到它自己的方法中,让 onkeypress 事件使用它,那么这将是我测试的方法,而不是尝试通过输入进行测试?

标签: javascript angular karma-jasmine keypress directive


【解决方案1】:

通常指令以这样的方式进行测试,以使其在实际组件中使用。所以你可以创建一个假组件来使用你的指令,然后你可以测试那个组件来处理你的指令。

这是大多数人建议的。

所以在你的测试文件中创建一个伪指令

// tslint:disable-next-line:data
@Component({
  selector: 'sd-test-layout',
  template: `
    <div sdAuthorized [permission]="'data_objects'">test</div>`
})
export class TestDecimalInputDirectiveComponent {

  @Input() permission;

  constructor() {
  }
}

然后在你每次使用TestBed之前创建组件实例,现在你就可以应用鼠标事件并在真实情况下测试它们了

TestBed.configureTestingModule({
  imports: [
    HttpModule,
    SharedModule
  ],
  declarations: [
    TestDecimalInputDirectiveComponent,
  ],
  providers: [
    {
      provide: ElementRef,
      useClass: MockElementRef
    },
    AuthorizedDirective,
  ]
}).compileComponents();

刚刚给你提示。您可以follow this link获取更多信息

【讨论】:

  • 他已经按照你说的做了。问题是他无法通过dispatchEvent 更改输入的值。所以他无法测试他的指令,该指令会通过阻止 keypress 事件来阻止添加非数字字符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-12
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 2021-09-20
  • 2023-03-18
相关资源
最近更新 更多