简体   繁体   English

如何在Angular 2单元测试中触发ngModel模型更新?

[英]How do I trigger a ngModel model update in an Angular 2 unit test?

I am attempting to test a component containing a text input element. 我试图测试包含文本输入元素的组件。 I want to verify that the state of the component changes as expected when the value of the text input changes. 我想验证当文本输入的值更改时组件的状态按预期更改。 The text input, of course, utilizes the ngModel directive (two-way binding). 当然,文本输入使用ngModel指令(双向绑定)。

Although the component works fine at runtime, I'm having trouble creating a valid, passing test. 虽然组件在运行时工作正常,但我在创建有效的传递测试时遇到了问题。 I have posted what I think should work below, and the test result following that. 我发布了我认为应该在下面工作的内容,以及之后的测试结果。

What am I doing wrong? 我究竟做错了什么?

TEST: 测试:

import {Component} from 'angular2/core';
import {describe, it, injectAsync, TestComponentBuilder} from 'angular2/testing';
import {FORM_DIRECTIVES} from 'angular2/common';
import {By} from 'angular2/platform/common_dom';

class TestComponent {
    static get annotations() {
        return [
            new Component({
                template: '<input type="text" [(ngModel)]="name" /><p>Hello {{name}}</p>',
                directives: [FORM_DIRECTIVES]
            })
        ]
    }
}

describe('NgModel', () => {
    it('should update the model', injectAsync([TestComponentBuilder], tcb => {
        return tcb
            .createAsync(TestComponent)
            .then(fixture => {
                fixture.nativeElement.querySelector('input').value = 'John';
                const inputElement = fixture.debugElement.query(By.css('input'));
                inputElement.triggerEventHandler('input', { target: inputElement.nativeElement });
                fixture.detectChanges();
                expect(fixture.componentInstance.name).toEqual('John');
            });
    }));
});

OUTPUT: OUTPUT:

Chrome 45.0.2454 (Mac OS X 10.10.5) NgModel should update the model FAILED
    Expected undefined to equal 'John'.
        at /Users/nsaunders/Projects/ng2-esnext-seed/src/test/ngmodel.spec.js!transpiled:41:52
        at ZoneDelegate.invoke (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:322:29)
        at Zone.run (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:218:44)
        at /Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:567:58
        at ZoneDelegate.invokeTask (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:355:38)
        at Zone.runTask (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:254:48)
        at drainMicroTaskQueue (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:473:36)
        at XMLHttpRequest.ZoneTask.invoke (/Users/nsaunders/Projects/ng2-esnext-seed/node_modules/angular2/bundles/angular2-polyfills.js:425:22)

Here's a way to do it: 这是一种方法:

import {Component} from 'angular2/core';
import {describe, it, inject, fakeAsync, tick, TestComponentBuilder} from 'angular2/testing';
import {FORM_DIRECTIVES} from 'angular2/common';

class TestComponent {
    static get annotations() {
        return [
            new Component({
                template: '<input type="text" [(ngModel)]="name" /><p>Hello {{name}}</p>',
                directives: [FORM_DIRECTIVES]
            })
        ]
    }
}

describe('NgModel', () => {
    it('should update the model', inject([TestComponentBuilder], fakeAsync(tcb => {
        let fixture = null;
        tcb.createAsync(TestComponent).then(f => fixture = f);
        tick();

        fixture.detectChanges();
        let input = fixture.nativeElement.querySelector('input');
        input.value = 'John';
        let evt = document.createEvent('Event');
        evt.initEvent('input', true, false);
        input.dispatchEvent(evt);
        tick(50);

        expect(fixture.componentInstance.name).toEqual('John');
    })));
});

NgModel listens to the input event to get notified about changes: NgModel侦听input事件以获得有关更改的通知:

dispatchEvent(inputElement, "input");
tick();

For other input elements other events might be used (checkbox, radio, option,...). 对于其他输入元素,可能会使用其他事件(复选框,无线电,选项......)。

I handled it this way: 我这样处理它:

import {TestBed, ComponentFixture, async} from "@angular/core/testing";
import {dispatchEvent} from "@angular/platform-browser/testing/browser_util";
import {FormsModule} from "@angular/forms";
import {By} from "@angular/platform-browser";
import {DebugElement} from "@angular/core";

describe('my.component', () => {

    let comp: MyComp,
        fixture: ComponentFixture<MyComp>;

    beforeEach(async(() => {
            TestBed.configureTestingModule({
                imports: [FormsModule],
                declarations: [
                    MyComp
                ]
            }).compileComponents().then(() => {
                fixture = TestBed.createComponent(MyComp);
                comp = fixture.componentInstance;
                fixture.detectChanges();
            });
        })
    );

    it('should handle inputs', () => {
        const sendInputs = (inputs: Array<DebugElement>) => {
            inputs.forEach(input => {
                dispatchEvent(input.nativeElement, 'input');
            });
            fixture.detectChanges();
            return fixture.whenStable();
        };

        let inputs: Array<DebugElement> = fixture.debugElement.queryAll(By.css('input'));

        // set username input value
        inputs[0].nativeElement.value = "Username";

        // set email input value
        inputs[1].nativeElement.value = "Email";

        sendInputs(inputs).then(() => {
            // emit click event to submit form
            fixture.nativeElement.querySelectorAll('button')[0].click();

            expect(comp.username).toBe("Username");
            expect(comp.email).toBe("Email");
        });
    });
});

And my html kinda looking like this: 我的html有点像这样:

<form (ngSubmit)="submit()">
    <input type="text" name="username" class="form-control" placeholder="username" required [(ngModel)]="username">
    <input type="email" name="email" class="form-control" placeholder="email" required [(ngModel)]="email">
    <button type="submit" class="btn btn-primary block full-width m-b">Submit</button>
</form>

I also suggest checking out angulars source code testing which helped me alot: Forms tests 我还建议检查angulars源代码测试,这有助于我很多: 表单测试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Angular 2+单元测试中触发键盘事件? - How do I trigger a keyboard event in an Angular 2+ unit test? NativeScript 2 / Angular 2数据绑定[(ngModel)]如何以编程方式更新TextField。 - NativeScript 2/ Angular 2 Data binding [(ngModel)] how do I update TextField programmatically. 如何使用Jasmine(Angular)单元测试下载文件? - How do I unit test downloading a file using Jasmine (Angular)? 我如何对使用 ngComponentOutlet 创建的角度组件进行单元测试? - How do i Unit test an angular component created with ngComponentOutlet? 如何对Angular HttpClient 204响应进行单元测试? - How do I unit test an Angular HttpClient 204 response? 如何为authguard单元测试canActivate of Angular? - How do I unit test canActivate of Angular for authguard? 如何在Angular单元测试中创建假的NgForm对象? - How do I create a fake NgForm object in an Angular unit test? 如何在 Angular 和 Jasmine 中对取消订阅进行单元测试? - How do I unit test unsubscribe in Angular and Jasmine? Angular2单元测试环境设置? 如何触发鼠标事件? - Angular2 unit test environment setup? How to do trigger mouse event? 如何在Angular2中触发模型更改事件? - How do I trigger an event on model change in Angular2?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM