2016-03-21 24 views
5

テキスト入力要素を含むコンポーネントをテストしようとしています。私は、テキスト入力の値が変化したときにコンポーネントの状態が期待通りに変化していることを確認したい。もちろん、テキスト入力はngModelディレクティブ(双方向バインディング)を利用します。Angular 2ユニットテストでngModelモデルを更新するにはどうすればよいですか?

コンポーネントは実行時に問題なく動作しますが、有効なテストを作成するのに問題があります。私は、以下で動作するはずのものとそれに続くテスト結果を掲載しました。

私は間違っていますか?

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:

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) 
+0

[ソースコード](https://github.com/angular/angular/blob/f9fb72fb0e9bcbda7aeebbf8321ce5d70d78ecee/modules/angular2/test/common/forms/integration_spec.ts#L824)を読むのはどうですか? –

+1

ありがとう、私は明らかに関連する例のソースを精練しながらこれを見落としていた。この例では、private API(angular2/testing_internalのdispatchEvent)のように見えるものを使用していますが、少なくとも私はfakeAsyncを試してみるという考えがありました。 –

答えて

2

NgModelは変更についての通知を得るためにinputイベントをlisten:他のイベントが使用されるかもしれない他の入力要素については

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

(チェックボックス、ラジオ、オプション、...)。ここで

+0

私はここでエラーを経験している: UNSPECIFIED_EVENT_TYPE_ERR:UNSPECIFIED_EVENT_TYPE_ERR:DOMイベントのconfig/SPEC-bundle.jsの例外0コードがどのように見えるかを見ることなく伝えるのは難しい(ライン49252) にdispatchEvent @ [ネイティブコード] –

+0

。 –

+0

上記のNick Saundersの回答はより正確です –

9

はそれを行うための方法です:

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'); 
    }))); 
}); 
+1

注:Event.initEventは非推奨です。代わりにEventコンストラクタを使用することをお勧めします。私はPhantomJSのために廃止予定のAPIを使用しなければなりませんでした。詳細については、https://developer.mozilla.org/en-US/docs/Web/API/Event/initEventとhttps://developer.mozilla.org/en-US/docs/Web/API/Event/Eventを参照してください。情報 –

1

私はそれをこのように処理:

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"); 
     }); 
    }); 
}); 

をそして、私の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> 

私もチェックをお勧め私を助けてくれた角度のソースコードテスト:Forms tests

+0

表示されていないdispatchEventというカスタム関数がありますか? dispatchEventは対象オブジェクトに対して呼び出されるはずです。 –

関連する問題