I've got a failing asynchronous angular component DOM test but it's synchronous equivalent fails and I don't understand why.
Here's the jasmine test:
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();
}));
});
Which creates the correct output in the synchronous case but incorrect in the asynchronous case:
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.'
I'm not sure if this has something to do with the angular-calendar component I'm using or if it's a more basic problem with my test code.
For reference here is my component, template and service code:
@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 : '';
}
}
template code:
<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>
service code:
@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);
}
}
In almost all cases, they can be used interchangeably, but using fakeAsync()/tick() combo is preferred unless you need to make an XHR call, in which case you MUST use async()/whenStable() combo, as fakeAsync() does not support XHR calls. For the most part they can be used interchangeably.
The TestBed. createComponent() method is used to create an instance of the AppComponent. The spec then uses expect and matcher functions to see if the component produces the expected behavior. As a result, the spec will either pass or fail.
You could write a setup function and call it at the beginning of each spec. But using Jasmine, you can declare code that is called before and after each spec, or before and after all specs. For this purpose, Jasmine provides four functions: beforeEach , afterEach , beforeAll and afterAll .
You can also use Jasmine as a library in your project. For example the following code imports and executes Jasmine: var Jasmine = require('jasmine'); var jasmine = new Jasmine(); jasmine. loadConfigFile('spec/support/jasmine.
I'm pretty sure that the reason this is not working is because of how you have defined your spy. It is defined outside of a beforeEach
block.
The way you have it now, your spy is created once, when the fixture starts up. And jasmine works by removing all old spies at the end of every test. So, by the second test, your spy no longer exists.
I would bet that if you switched your test order, then you would see the async test working, but the sync one not.
To fix, simply move this into a beforeEach block:
const absenceService = jasmine.createSpyObj('AbsenceService', ['findAbsences']);
absenceService.findAbsences.and.returnValue(of([{}]));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With