我指的是这个Plunker,它演示了Angular 2/4中的单元测试表单提交。如果我正确理解了代码,那么测试会验证表单是否正确提交,并且最终(通过间谍)提交的内容会调用this.formSubmit.emit();
。
为什么测试不能验证组件的doFormSubmit
方法也被调用?
我希望测试(以及我自己的表单测试)验证(以相同的期望)表单提交和组件方法。
我看到其他人的测试,直接组件的方法测试正确调用没有表单和我看到这样的测试得到正确的最终结果但不验证调用的确切方法。但是,我没有看到任何兼具两种测试的测试。
是否有一些async / fakeasync / tick问题,我没有看到会阻止该测试?
<form #form="ngForm" (ngSubmit)="doFormSubmit()">
<div>
<div class="clearfix m-b-1">
<label class="form-control-static" for="reviewContent" i18n>l.product.show.reviewForm.content</label>
<div>
<textarea
class="form-control"
id="reviewContent"
name="content"
required
[(ngModel)]="model.content"
#content="ngModel"
></textarea> {{model.content}}
</div>
</div>
</div>
<div>
<div class="clearfix m-b-1">
<label class="form-control-static" for="reviewRating" i18n>l.product.show.reviewForm.rating</label>
<div>
<input
type="number"
class="form-control l-number-input"
id="reviewRating"
name="rating"
required
[(ngModel)]="model.rating"
#rating="ngModel"
/>{{model.rating}}
</div>
</div>
</div>
<div>
<button
type="submit"
class="btn btn-primary btn-lg btn-block"
[disabled]="!form.form.valid"
i18n
>l.product.show.reviewForm.submit</button>
</div>
</form>
{{doFormSubmitCalled}}
import { Component, Input, ViewChild, Output, EventEmitter } from '@angular/core';
import { NgForm } from '@angular/forms'
@Component({
selector: 'home-comp',
templateUrl: `app/test.component.html`
})
export class TestComponent {
@Input()
public model: any;
doFormSubmitCalled = false;
@Output()
public formSubmit = new EventEmitter<any>();
public doFormSubmit() {
//alert.log("doFormSubmit called");
this.formSubmit.emit();
this.doFormSubmitCalled = true;
}
}
import { FormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, tick, async, fakeAsync } from '@angular/core/testing';
import { TestComponent } from './test.component';
function newEvent(eventName: string, bubbles = false, cancelable = false) {
let evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
evt.initCustomEvent(eventName, bubbles, cancelable, null);
return evt;
}
describe('ProductShowReviewFormComponent', () => {
let fixture: ComponentFixture<TestComponent>;
let comp: TestComponent;
let submitSpy: jasmine.Spy;
// SO: spy on component's method
let componentMethodSpy: jasmine.Spy;
let contentInput: HTMLTextAreaElement;
let ratingInput: HTMLInputElement;
let form: DebugElement;
beforeEach(async(() => {
TestBed
.configureTestingModule({
imports: [FormsModule],
declarations: [TestComponent],
})
.compileComponents()
;
}));
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(TestComponent);
comp = fixture.componentInstance;
submitSpy = spyOn(comp.formSubmit, 'emit');
contentInput = fixture.debugElement.query(By.css('#reviewContent')).nativeElement;
ratingInput = fixture.debugElement.query(By.css('#reviewRating')).nativeElement;
form = fixture.debugElement.query(By.css('form'));
// SO: component's method spy - EDIT: MUST CALL THROUGH
componentMethodSpy = spyOn(component, 'doFormSubmit').and.callThrough();
comp.model = { content: '', rating: '' };
fixture.detectChanges();
tick();
}));
it('should modify message', fakeAsync(() => {
const expectedContent = 'The longest content.';
const expectedRating = '4';
contentInput.value = expectedContent;
contentInput.dispatchEvent(newEvent('input'));
ratingInput.value = expectedRating;
ratingInput.dispatchEvent(newEvent('input'));
fixture.detectChanges();
tick();
form.triggerEventHandler('submit', null);
expect(submitSpy.calls.any()).toBe(true);
expect(comp.model.content).toBe(expectedContent);
expect(comp.model.rating.toString()).toBe(expectedRating);
// SO: was the Component's method called
expect(componentMethodSpy).toHaveBeenCalled();
}));
});