angular - Enhetstestning av värdet på Observable som returneras från tjänst (med async pipe)


När jag kör Angular/Jasmine/Karma har jag en komponent som använder en tjänst för att ställa in värdet på en Observable 'items'-array. Jag visar detta med en async-pipe. Fungerar utmärkt.

Nu försöker jag sätta upp ett enhetstest och fick det att gå igenom, men jag är inte säker på att jag korrekt verifierar att 'items'-arrayen får rätt värde.

Här är den relevanta komponentens .html och .ts :

export class ViperDashboardComponent implements OnInit, OnDestroy {

    items: Observable<DashboardItem[]>;

    constructor(private dashboardService: ViperDashboardService) { }

    ngOnInit() {
        this.items = this.dashboardService.getDashboardItems();
    }
}
    <ul class="list-group">
        <li class="list-group-item" *ngFor="let item of items | async">
            <h3>{{item.value}}</h3>
            <p>{{item.detail}}</p>
        </li>
    </ul>

Och min component.spec.ts:

    beforeEach(() => {
        fixture = TestBed.createComponent(ViperDashboardComponent);
        component = fixture.componentInstance;

        viperDashboardService =        
  fixture.debugElement.injector.get(ViperDashboardService);

        mockItems = [
            { key: 'item1', value: 'item 1', detail: 'This is item 1' },
            { key: 'item2', value: 'item 2', detail: 'This is item 2' },
            { key: 'item3', value: 'item 3', detail: 'This is item 3' }
        ];

        spy = spyOn(viperDashboardService, 'getDashboardItems')
            .and.returnValue(Observable.of<DashboardItem[]>(mockItems));

    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('should call getDashboardItems after component initialzed', () => {
        fixture.detectChanges();
        expect(spy.calls.any()).toBe(true, 'getDashboardItems should be called');
    });

    it('should show the dashboard after component initialized', () => {
        fixture.detectChanges();
        expect(component.items).toEqual(Observable.of(mockItems));
    });

Specifikt skulle jag vilja veta:

1) Jag började med att skapa ett asynkront "it"-test, men blev förvånad när det inte fungerade. Varför fungerar ett synkront test när jag arbetar med asynkrona dataströmmar?

2) När jag kontrollerar ekvivalensen mellan component.items och Observable.of(mockItems), testar jag verkligen att värdena är lika? Eller testar jag bara att de båda är Observables? Finns det ett bättre sätt?

Svar #1

Angular tillhandahåller verktygen för att testa asynkrona värden. Du kan använda async-verktyget med fixture.whenStable-metoden eller fakeAsync-verktyget med tick()-funktionen. Sedan, genom att använda DebugElement, kan du faktiskt fråga din mall för att säkerställa att värdena laddas korrekt.

Båda metoderna för testning kommer att fungera.

Använder async-verktyget med whenStable:

Behåll din setup samma, du är redo att köra där. Du kommer behöva lägga till lite kod för att få tag på listans debug-element. I din beforeEach:

const list = fixture.debugElement.query(By.css('list-group'));

Då kan du gräva i den listan och få enskilda objekt. Jag kommer inte att gå in för mycket på hur man använder DebugElement eftersom det ligger utanför omfattningen av denna fråga. Läs mer här: https://angular.io/guide/testing#componentfixture-debugelement-and-querybycss.

Sedan i ditt enhetstest:

 it('should get the dashboard items when initialized', async(() => {
        fixture.detectChanges();
        
        fixture.whenStable().then(() => { // wait for your async data
          fixture.detectChanges(); // refresh your fake template
          /* 
             now here you can check the debug element for your list 
             and see that the items in that list correctly represent 
             your mock data 
             e.g. expect(listItem1Header.textContent).toEqual('list item 1');
           */
        }
    }));

Använda fakeAsync-verktyget med tick:

it('should get the dashboard items when initialized', fakeAsync(() => {
        fixture.detectChanges();
        tick(); // wait for async data
        fixture.detectChanges(); // refresh fake template
        /* 
           now here you can check the debug element for your list 
           and see that the items in that list correctly represent 
          your mock data 
           e.g. expect(listItem1Header.textContent).toEqual('list item 1');
        */
}));

Så sammanfattningsvis, ta inte bort async-pipen från din mall bara för att göra testning enklare. async-pipen är ett utmärkt verktyg och gör mycket städning åt dig, dessutom har Angular-teamet tillhandahållit några mycket användbara testverktyg för just detta användningsområde. Förhoppningsvis fungerar en av teknikerna ovan. Det låter som att användning av DebugElement och ett av de ovan nämnda verktygen kommer att hjälpa dig mycket :)

Relaterade artiklar