为什么异步 angular 单元测试找不到 DOM 元素?
Why doesn't async angular unit-test find DOM element?
我有一个失败的异步 angular 组件 DOM 测试,但它的同步等效失败,我不明白为什么。
这是茉莉花测试:
describe('Availability Component', () => {
let fixture: ComponentFixture<AvailabilityComponent>;
const absenceService = jasmine.createSpyObj('AbsenceService', ['findAbsences']);
absenceService.findAbsences.and.returnValue(of([{}]));
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AvailabilityComponent],
imports: [CalendarModule.forRoot()],
providers: [{provide: AbsenceService, useValue: absenceService}]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AvailabilityComponent);
});
const printAbsenceReasons = function () {
console.log(fixture.debugElement.queryAll(By.css('.calendarAbsence'))
.map(e => e.nativeElement.textContent)
.join(','));
};
it('should synchronously find absences in calendar view', () => {
fixture.detectChanges();
console.log('synchronous test');
printAbsenceReasons();
});
it('should asynchronously find absences in calendar view', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
tick();
console.log('asynchronous test');
printAbsenceReasons();
}));
});
在同步情况下创建正确的输出但在异步情况下不正确:
LOG: 'processing absences'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'synchronous test'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
ERROR: 'Spec 'Availability Component should synchronously find absences in calendar view' has no expectations.'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'processing absences'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'asynchronous test'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
LOG: ''
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
ERROR: 'Spec 'Availability Component should asynchronously find absences in calendar view' has no expectations.'
我不确定这是否与我正在使用的 angular-calendar 组件有关,还是我的测试代码存在更基本的问题。
我的组件、模板和服务代码供参考:
@Component({
selector: 'app-availability',
templateUrl: './availability.component.html',
styleUrls: ['availability.component.scss']
})
export class AvailabilityComponent implements OnInit {
viewDate: Date = new Date();
absenceEvents: CalendarEvent[] = [];
constructor(private absenceService: AbsenceService) {
}
ngOnInit() {
this.getAbsences();
}
getAbsences() {
this.absenceService.findAbsences()
.subscribe(ignored => {
console.log('processing absences');
this.absenceEvents = [{
start: new Date(2018, 3, 29), title: 'A'
}];
});
}
getAbsence(events: CalendarEvent[]) {
return events[0] ? events[0].title : '';
}
}
模板代码:
<div>
<div>
<mwl-calendar-month-view
[viewDate]="viewDate"
[events]="absenceEvents"
[cellTemplate]="availabilityCellTemplate">
</mwl-calendar-month-view>
</div>
<ng-template #availabilityCellTemplate let-day="day">
<div class="calendarAbsence">{{ getAbsence(day.events) }}</div>
</ng-template>
</div>
服务代码:
@Injectable()
export class AbsenceService {
private url = environment.APP_SHIFT_SERVICE_BASE_URL + '/api/absences';
constructor(private http: HttpClient) {
}
findAbsences(): Observable<Absence[]> {
console.error('Actual findAbsences() called');
return this.http.get<Absence[]>(this.url);
}
}
我很确定这不起作用的原因是您定义间谍的方式。它是在 beforeEach
块之外定义的。
按照你现在的方式,你的 spy 只在 fixture 启动时创建一次。茉莉花的工作原理是在每次测试结束时删除所有旧间谍。所以,通过第二次测试,你的间谍已经不存在了。
我敢打赌,如果您切换测试顺序,那么您会看到异步测试有效,但同步测试无效。
要修复,只需将其移动到 beforeEach 块中:
const absenceService = jasmine.createSpyObj('AbsenceService', ['findAbsences']);
absenceService.findAbsences.and.returnValue(of([{}]));
不幸的是,这似乎与 fakeAsync 区域有关,而不是与异步测试有关。
我已经设法让一个工作的异步测试与 async
一起工作,而不是 fakeAsync
:
it('should asynchronously find absences in calendar view', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
console.log('asynchronous test');
printAbsenceReasons(fixture);
expect(getAbsenceElements(fixture).length).toEqual(35);
});
});
}));
我调试了失败的 fakeAsync
和成功的同步测试。他们都在库 "calendar-utils" 中调用了一个名为 getMonthView
的方法,作者与 "angular-calendar" 相同:
https://github.com/mattlewis92/calendar-utils/blob/master/src/calendar-utils.ts
在这个 method 中,日期参数和其他日期计算本身似乎都出错了。
我目前只能假设它与此有关known issue in zone.js。我在 Angular 5.2,但我认为它仍然相关。无论如何,我确信我的测试代码基本上是正确的,但问题出在其他地方。
我有一个失败的异步 angular 组件 DOM 测试,但它的同步等效失败,我不明白为什么。
这是茉莉花测试:
describe('Availability Component', () => {
let fixture: ComponentFixture<AvailabilityComponent>;
const absenceService = jasmine.createSpyObj('AbsenceService', ['findAbsences']);
absenceService.findAbsences.and.returnValue(of([{}]));
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AvailabilityComponent],
imports: [CalendarModule.forRoot()],
providers: [{provide: AbsenceService, useValue: absenceService}]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AvailabilityComponent);
});
const printAbsenceReasons = function () {
console.log(fixture.debugElement.queryAll(By.css('.calendarAbsence'))
.map(e => e.nativeElement.textContent)
.join(','));
};
it('should synchronously find absences in calendar view', () => {
fixture.detectChanges();
console.log('synchronous test');
printAbsenceReasons();
});
it('should asynchronously find absences in calendar view', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
tick();
console.log('asynchronous test');
printAbsenceReasons();
}));
});
在同步情况下创建正确的输出但在异步情况下不正确:
LOG: 'processing absences'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'synchronous test'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'A,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
ERROR: 'Spec 'Availability Component should synchronously find absences in calendar view' has no expectations.'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 0 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'processing absences'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
LOG: 'asynchronous test'
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
LOG: ''
HeadlessChrome 0.0.0 (Mac OS X 10.13.4): Executed 1 of 51 SUCCESS (0 secs / 0 secs)
ERROR: 'Spec 'Availability Component should asynchronously find absences in calendar view' has no expectations.'
我不确定这是否与我正在使用的 angular-calendar 组件有关,还是我的测试代码存在更基本的问题。
我的组件、模板和服务代码供参考:
@Component({
selector: 'app-availability',
templateUrl: './availability.component.html',
styleUrls: ['availability.component.scss']
})
export class AvailabilityComponent implements OnInit {
viewDate: Date = new Date();
absenceEvents: CalendarEvent[] = [];
constructor(private absenceService: AbsenceService) {
}
ngOnInit() {
this.getAbsences();
}
getAbsences() {
this.absenceService.findAbsences()
.subscribe(ignored => {
console.log('processing absences');
this.absenceEvents = [{
start: new Date(2018, 3, 29), title: 'A'
}];
});
}
getAbsence(events: CalendarEvent[]) {
return events[0] ? events[0].title : '';
}
}
模板代码:
<div>
<div>
<mwl-calendar-month-view
[viewDate]="viewDate"
[events]="absenceEvents"
[cellTemplate]="availabilityCellTemplate">
</mwl-calendar-month-view>
</div>
<ng-template #availabilityCellTemplate let-day="day">
<div class="calendarAbsence">{{ getAbsence(day.events) }}</div>
</ng-template>
</div>
服务代码:
@Injectable()
export class AbsenceService {
private url = environment.APP_SHIFT_SERVICE_BASE_URL + '/api/absences';
constructor(private http: HttpClient) {
}
findAbsences(): Observable<Absence[]> {
console.error('Actual findAbsences() called');
return this.http.get<Absence[]>(this.url);
}
}
我很确定这不起作用的原因是您定义间谍的方式。它是在 beforeEach
块之外定义的。
按照你现在的方式,你的 spy 只在 fixture 启动时创建一次。茉莉花的工作原理是在每次测试结束时删除所有旧间谍。所以,通过第二次测试,你的间谍已经不存在了。
我敢打赌,如果您切换测试顺序,那么您会看到异步测试有效,但同步测试无效。
要修复,只需将其移动到 beforeEach 块中:
const absenceService = jasmine.createSpyObj('AbsenceService', ['findAbsences']);
absenceService.findAbsences.and.returnValue(of([{}]));
不幸的是,这似乎与 fakeAsync 区域有关,而不是与异步测试有关。
我已经设法让一个工作的异步测试与 async
一起工作,而不是 fakeAsync
:
it('should asynchronously find absences in calendar view', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
console.log('asynchronous test');
printAbsenceReasons(fixture);
expect(getAbsenceElements(fixture).length).toEqual(35);
});
});
}));
我调试了失败的 fakeAsync
和成功的同步测试。他们都在库 "calendar-utils" 中调用了一个名为 getMonthView
的方法,作者与 "angular-calendar" 相同:
https://github.com/mattlewis92/calendar-utils/blob/master/src/calendar-utils.ts
在这个 method 中,日期参数和其他日期计算本身似乎都出错了。
我目前只能假设它与此有关known issue in zone.js。我在 Angular 5.2,但我认为它仍然相关。无论如何,我确信我的测试代码基本上是正确的,但问题出在其他地方。