Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't async angular unit-test find DOM element?

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);
    }
}
like image 417
kossmoboleat Avatar asked May 09 '18 07:05

kossmoboleat


People also ask

What is the difference between async ()' and fakeAsync?

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.

Which TestBed method is used to create an Angular component under test?

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.

Which of the following functions should be used to execute some piece of code before execution of each Jasmine test case in Angular?

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 .

Which of the following code of Jasmine is used as a library in your project?

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.


1 Answers

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([{}]));
like image 164
Andrew Eisenberg Avatar answered Oct 15 '22 23:10

Andrew Eisenberg