2016-03-21 12 views
5

Sto tentando di testare un componente contenente un elemento di input di testo. Voglio verificare che lo stato del componente cambi come previsto quando cambia il valore dell'ingresso del testo. L'input di testo, ovviamente, utilizza la direttiva ngModel (binding a due vie).Come attivare un aggiornamento del modello ngModel in un test di unità Angular 2?

Sebbene il componente funzioni correttamente in fase di esecuzione, ho difficoltà a creare un test valido e passeggero. Ho pubblicato quello che penso dovrebbe funzionare sotto, e il risultato del test successivo.

Cosa sto sbagliando?

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

USCITA:

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

Che dire leggendo il [codice sorgente] (https://github.com/angular/angular/blob/f9fb72fb0e9bcbda7aeebbf8321ce5d70d78ecee/modules/angular2/test/common/forms/integration_spec.ts#L824)? –

+1

Grazie, apparentemente ho trascurato questo mentre setacciavo la fonte per un esempio pertinente. È un peccato che questo esempio usi quella che sembra essere un'API privata (dispatchEvent da angular2/testing_internal), ma mi ha dato l'idea di provare almeno fakeAsync. –

risposta

2

NgModel ascolta l'evento input a ricevere la notifica dei cambiamenti:

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

Per gli altri elementi di input potrebbero essere utilizzati altri eventi (casella di controllo, radio, opzione, ...).

+0

Sto vivendo un errore qui: UNSPECIFIED_EVENT_TYPE_ERR: UNSPECIFIED_EVENT_TYPE_ERR: DOM eventi di eccezione 0 in config/Spec-bundle.js (linea 49252) dispatchEvent @ [codice nativo] –

+0

Difficile dire senza vedere ciò che il codice è simile. –

+0

La risposta di Nick Saunders sopra sembra più accurata –

9

Ecco un modo per farlo:

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

Nota: Event.initEvent è deprecato; probabilmente vuoi usare il costruttore Event. Ho dovuto usare l'API deprecata perché PhantomJS. Vedi https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent e https://developer.mozilla.org/en-US/docs/Web/API/Event/Event per ulteriori informazioni Informazioni. –

1

ho gestito in questo modo:

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

E il mio html po 'simile a questo:

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

Suggerisco anche verifica out angulars test del codice sorgente che mi ha aiutato molto: Forms tests

+0

Hai una funzione personalizzata chiamata dispatchEvent che non è mostrata? Perché penso che dispatchEvent dovrebbe essere chiamato sull'oggetto di destinazione. –

Problemi correlati