Angular 2 自定义验证单元测试

Posted

技术标签:

【中文标题】Angular 2 自定义验证单元测试【英文标题】:Angular 2 Custom validation unit testing 【发布时间】:2017-02-16 00:38:15 【问题描述】:

我正在编写自定义角度(Angular 2.0.0)验证,遵循本指南https://angular.io/docs/ts/latest/cookbook/form-validation.html#!#custom-validation。

@Directive(
  selector: '[ngModel][emailValidator]',
  providers: [provide: NG_VALIDATORS, useExisting: EmailValidatorDirective, multi: true]
)
export class EmailValidatorDirective implements Validator 

现在我正在尝试将单元测试添加到我的自定义验证指令中。

beforeEach(() => 
      fixture = TestBed.createComponent(EmailComponent);
      component = fixture.componentInstance;
      de = fixture.debugElement;
      el = de.nativeElement;
      component = de.componentInstance;
      emailField = de.query(By.css('input')).nativeElement;
    );

我正在访问所有这些对象,但没有人知道我输入的有效性的任何信息。有谁知道如何在单元测试中访问我的输入的 NgControl,或者我如何检查有效/无效(自定义验证)输入字段。

【问题讨论】:

【参考方案1】:

您需要做的是获取具有NgForm 的注射器。我花了一段时间才弄清楚。我以为你可以从 debugElement 那里得到它,但看起来你需要从它的孩子那里得到它1

let form: NgForm = fixture.debugElement.children[0].injector.get(NgForm);

您可以从表单组中获取单独的控件

let emailControl = form.control.get('email');
expect(emailControl.valid).toBe(true);

或者您可以只检查表单中的特定错误

expect(form.control.hasError('emailInvalid', ['email'])).toBe(true);

下面是完整的测试

import  Component, forwardRef, Directive  from '@angular/core';
import  TestBed, getTestBed, async  from '@angular/core/testing';
import  FormsModule, NG_VALIDATORS, Validator, AbstractControl, NgForm  from '@angular/forms';
import  dispatchEvent  from '@angular/platform-browser/testing/browser_util';
import  By  from '@angular/platform-browser';

@Directive(
  selector: '[ngModel][validEmail]',
  providers: [
    
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => EmailValidatorDirective),
      multi: true
    
  ]
)
class EmailValidatorDirective implements Validator 

  validate(c: AbstractControl):  [key: string]: any  
    if (c.value !== 'peeskillet@***.com') 
      return  notPeeskillet: true ;
    
    return null;
  


@Component(
  template: `
    <form>
      <input name="email" [ngModel]="email" validEmail />
    </form>
  `
)
class TestComponent 
  email;


describe('component: TestComponent', () => 
  beforeEach(() => 
    TestBed.configureTestingModule(
      imports: [FormsModule],
      declarations: [TestComponent, EmailValidatorDirective]
    );
  );

  it('should validate', async(() => 
    let fixture = TestBed.createComponent(TestComponent);
    let comp = fixture.componentInstance;
    let debug = fixture.debugElement;
    let input = debug.query(By.css('[name=email]'));

    fixture.detectChanges();
    fixture.whenStable().then(() => 
      input.nativeElement.value = 'bad@email.com';
      dispatchEvent(input.nativeElement, 'input');
      fixture.detectChanges();

      let form: NgForm = debug.children[0].injector.get(NgForm);
      let control = form.control.get('email');
      expect(control.hasError('notPeeskillet')).toBe(true);
      expect(form.control.valid).toEqual(false);
      expect(form.control.hasError('notPeeskillet', ['email'])).toEqual(true);

      input.nativeElement.value = 'peeskillet@***.com';
      dispatchEvent(input.nativeElement, 'input');
      fixture.detectChanges();

      expect(control.hasError('notPeeskillet')).toBe(false);
      expect(form.control.valid).toEqual(true);
      expect(form.control.hasError('notPeeskillet', ['email'])).toEqual(false);
    );
  ));
);

1 - Found it in the source code tests

【讨论】:

browser_util 已移至import dispatchEvent from '@angular/platform-browser/testing/src/browser_util'; 改用内置的 dispatch 事件【参考方案2】:

上述最佳答案对我不起作用,但我能够通过以下方式测试我的自定义验证器:

const associateRateControl = component.distributionSettingsForm.controls['associateRate'];
associateRateControl.setValue('plus ultra');
fixture.detectChanges();
expect(component.distributionSettingsForm.hasError('noCatalog')).toEqual(true);

【讨论】:

【参考方案3】:

关于该主题的另一个提示。 Paul Samsotha answer 我认为是正确的并且非常有帮助,但是 TBH 你不需要导入任何库。我们可以使用nativeElementdispatchEvent,让我们的生活更轻松。

beforeEach(() => 
    const input = fixture.debugElement.query(By.css("input")).nativeElement;
    fixture.detectChanges();
    input.value = "test";
    input.dispatchEvent(new Event("input"));
    fixture.detectChanges();
);

it("should display the password strength indicator", () => 
    const form = fixture.componentInstance.myMarvelousForm;
    expect(form.dirty).toBeTruthy();
);

【讨论】:

以上是关于Angular 2 自定义验证单元测试的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2单元测试:自定义管道错误找不到管道

Angular 2 - 单元测试绑定到嵌套的自定义表单控件

Angular 单元测试表单验证

angular2 使用主机组件进行单元测试

带有自定义类型的角度单元测试给出了找不到命名空间

在单元测试用例中模拟 Angular $window