2016-03-21 10 views
5

Próbuję przetestować składnik zawierający element wprowadzania tekstu. Chcę sprawdzić, czy stan składnika zmienia się zgodnie z oczekiwaniami, gdy zmienia się wartość wprowadzanego tekstu. Wprowadzanie tekstu oczywiście wykorzystuje dyrektywę ngModel (bindowanie dwukierunkowe).Jak wyzwalać aktualizację modelu ngModel w teście jednostki Angular 2?

Mimo że komponent działa poprawnie w środowisku wykonawczym, nie mogę utworzyć poprawnego, zaliczającego testu. Opublikowalem to, co moim zdaniem powinno dzialac ponizej, a wynik testu nastepuje po tym.

Co robię źle?

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

WYJŚCIE:

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

Co powiesz na czytanie [kodu źródłowego] (https://github.com/angular/angular/blob/f9fb72fb0e9bcbda7aeebbf8321ce5d70d78ecee/modules/angular2/test/common/forms/integration_spec.ts#L824)? –

+1

Dzięki, najwyraźniej przeoczyłem to podczas wyszukiwania źródła dla odpowiedniego przykładu. Szkoda, że ​​w tym przykładzie użyto czegoś, co wydaje się być prywatnym API (dispatchEvent from angular2/testing_internal), ale dałem mi pomysł wypróbowania przynajmniej fakeAsync. –

Odpowiedz

2

NgModel słucha przypadku input, aby otrzymywać powiadomienia o zmianach:

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

Dla innych elementów wejściowych mogą być stosowane inne wydarzenia (pole wyboru, radio, opcja, ...).

+0

mam wystąpił błąd tutaj: UNSPECIFIED_EVENT_TYPE_ERR: UNSPECIFIED_EVENT_TYPE_ERR: DOM Events Wyjątek 0 w config/SPEC-bundle.js (linia 49252) dispatchEvent @ [natywny kod] –

+0

Trudno powiedzieć, nie widząc co Twój kod wygląda. –

+0

Nick Saunders odpowiedź powyżej wydaje się być bardziej dokładna –

9

Oto sposób, aby to zrobić:

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

Uwaga: Event.initEvent jest przestarzałe; prawdopodobnie będziesz chciał użyć konstruktora zdarzeń. Musiałem użyć przestarzałego API, ponieważ PhantomJS. Zobacz https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent i https://developer.mozilla.org/en-US/docs/Web/API/Event/Event, aby uzyskać więcej informacji info. –

1

I obchodzić to tak:

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

A mój html trochę wygląda tak:

<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> 

Proponuję także sprawdzenie test kodu źródłowego angulars, który pomógł mi dużo: Forms tests

+0

Czy masz niestandardową funkcję o nazwie dispatchEvent, która nie jest wyświetlana? Ponieważ myślę, że dispatchEvent ma zostać wywołany na obiekcie docelowym. –

Powiązane problemy