带表格的单元测试角度指令

时间:2018-10-18 14:49:34

标签: angular jasmine

我有一个具有form属性的指令。指令放置在表单的“提交”按钮上,并侦听单击事件。单击“提交”按钮时,伪指令将检查表单是否有效,如果无效,则将click事件冒泡到ngSubmit处理程序中。它还将每个表单控件标记为脏的,以便显示验证消息。

该指令工作正常,我想添加单元测试,但是我不知道如何设置包含提交按钮的表单。到目前为止,这是我的测试,但是我无法弄清楚该指令所采用的NgForm与我创建的包含按钮的伪造形式之间的联系。

describe('ValidateBeforeSubmitDirective', () => {

    let fakeSubmitButtonRef: ElementRef<HTMLButtonElement>;
    let fakeForm: HTMLFormElement;

    beforeEach(() => {
        const fakeSubmitButton = document.createElement('BUTTON') as HTMLButtonElement;
        fakeSubmitButton.type = 'submit';
        fakeSubmitButtonRef = new ElementRef(fakeSubmitButton);

        fakeForm = document.createElement('form');
        fakeForm.appendChild(fakeSubmitButton);
    });

    it('should bubble click event to the submit method if form is valid', () => {
        //arrange
        const directive = new ValidateBeforeSubmitDirective(fakeSubmitButtonRef);
        directive.form = new NgForm([], []);
        spyOn(directive.form, 'ngSubmit');
        expect(directive.form.valid).toBe(true, 'Test has been set up incorrectly, the form should be valid for this test.');

        //*** What do I need to do to link my NgForm with fakeForm? ***

        //act
        fakeSubmitButtonRef.nativeElement.click();

        //assert
        expect(directive.form.ngSubmit).toHaveBeenCalled();
    });
});

作为参考,这是我的指令代码:

@Directive({
    selector: '[appValidateBeforeSubmit]'
})
export class ValidateBeforeSubmitDirective {

    /**
     * @param element This will be the element on which the directive is being used.
     */
    constructor(private readonly element: ElementRef<HTMLButtonElement>) {  }

    @Input('appValidateBeforeSubmit')
    form: NgForm;

    @HostListener('click', ['$event'])
    private onClick(event: Event) {

        if (!this.form.valid) {
            Object.keys(this.form.controls).forEach(key => {
                this.form.controls[key].markAsDirty();
            });
        }

        return this.form.valid; //if false, this will prevent the event from bubbling up to the ngSubmit handler
    }
}

它的用法如下:

<form #componentTypeForm="ngForm" (ngSubmit)="ok()">
    <button type="submit" [appValidateBeforeSubmit]="componentTypeForm">Submit</button>
</form>

有什么想法可以在测试中设置表单,以使按钮click事件冒泡到表单的ngSubmit处理程序吗?

1 个答案:

答案 0 :(得分:0)

有人确实发布了一个答案,使我处于正确的位置,但是他们可悲地删除了他们的答案,所以我不能给他们功劳。他们指出directives should be tested using a dummy component是在测试文件中定义的。我创建了一个组件,该组件的模板是带有输入的表单,并且已经能够通过测试该组件来测试指令的行为。

这是我的工作测试代码:

import { NgForm, FormsModule } from '@angular/forms';
import { Component } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { ValidateBeforeSubmitDirective } from './validate-before-submit.directive';

@Component({
    //The input on this form is required, so we can easily set the form validity by giving it an empty/non-empty string.
    template: `<form #testForm="ngForm">
                   <input name="providedValue" [(ngModel)]="providedValue" type="text" required />
                   <button type="submit" [appValidateBeforeSubmit]="testForm">Submit</button>
               </form>`
})
class TestValidateBeforeSubmitComponent {
    providedValue: string;
}

describe('ValidateBeforeSubmitDirective', () => {
    let form: NgForm;
    let fixture: ComponentFixture<TestValidateBeforeSubmitComponent>;

    beforeEach(() => {
        fixture = TestBed.configureTestingModule({
            declarations: [ValidateBeforeSubmitDirective, TestValidateBeforeSubmitComponent],
            imports: [FormsModule],
            providers: [NgForm, HTMLButtonElement]
        }).createComponent(TestValidateBeforeSubmitComponent);

        const formElement = fixture.debugElement.children[0];
        form = formElement.injector.get(NgForm);

        fixture.detectChanges(); // initial binding
    });

    it('should allow submission of a valid form', async(() => {
        //arrange
        fixture.componentInstance.providedValue = 'Arbitrary content'; //valid value
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(form.valid).toBe(true, 'Test has been set up incorrectly, form should be valid.');

            //act
            const buttonElement = fixture.debugElement.children[0].children[1];
            const button = buttonElement.nativeElement;
            button.click();

            //assert
            expect(form.submitted).toBe(true, 'Clicking the submit button on a valid form should have submitted the form.');
        });
    }));

    it('should prevent submission of an invalid form', async(() => {
        //arrange
        fixture.componentInstance.providedValue = ''; //invalid value
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(form.valid).toBe(false, 'Test has been set up incorrectly, form should be invalid.');

            //act
            const buttonElement = fixture.debugElement.children[0].children[1];
            const button = buttonElement.nativeElement;
            button.click();

            //assert
            expect(form.submitted).toBe(false, 'The directive should prevent an invalid form from submitting when the submit button is clicked.');
        });
    }));
});