2016-07-25 5 views
5

Я перехожу наше приложение Angular2 использовать RC4, и я начал получать ошибку на моих модульных тестов:Запуск Angular2 тестов, которые вызывают ошибки SetTimeout с «Не удается использовать setInterval внутри теста зоны асинхронным»

Не может использовать setInterval внутри теста асинхронной зоны

Моего виджет делает запрос на данные от своего метода ngOnInit и выставляет индикатор загрузки в то время, что запрос был сделан. Мой издевательский сервис возвращает некоторые данные после 1 мс.

Вот упрощенная версия, которая подвергает неполадку

import { inject, async, TestComponentBuilder, ComponentFixture} from '@angular/core/testing'; 
import {Http, Headers, RequestOptions, Response, HTTP_PROVIDERS} from '@angular/http'; 
import {provide, Component} from '@angular/core'; 

import {Observable} from "rxjs/Rx"; 

class MyService { 
    constructor(private _http: Http) {} 
    getData() { 
     return this._http.get('/some/rule').map(resp => resp.text()); 
    } 
} 

@Component({ 
    template: `<div> 
     <div class="loader" *ngIf="_isLoading">Loading</div> 
     <div class="data" *ngIf="_data">{{_data}}</div> 
    </div>` 
}) 
class FakeComponent { 
    private _isLoading: boolean = false; 
    private _data: string = ''; 

    constructor(private _service: MyService) {} 

    ngOnInit() { 
     this._isLoading = true; 
     this._service.getData().subscribe(data => { 
      this._isLoading = false; 
      this._data = data; 
     }); 
    } 
} 

describe('FakeComponent',() => { 
    var service = new MyService(null); 
    var _fixture:ComponentFixture<FakeComponent>; 

    beforeEach(async(inject([TestComponentBuilder], (tcb:TestComponentBuilder) => { 
     return tcb 
      .overrideProviders(FakeComponent, [ 
       HTTP_PROVIDERS, 
       provide(MyService, {useValue: service}), 
      ]) 
      .createAsync(FakeComponent) 
      .then((fixture:ComponentFixture<FakeComponent>) => { 
       _fixture = fixture; 
      }); 
    }))); 

    it('Shows loading while fetching data', (cb) => { 
     // Make the call to getData take one ms so we can verify its state while the request is pending 
     // Error occurs here, when the widget is initialized and sends out an XHR 
     spyOn(service, 'getData').and.returnValue(Observable.of('value').delay(1)); 
     _fixture.detectChanges(); 
     expect(_fixture.nativeElement.querySelector('.loader')).toBeTruthy(); 
     // Wait a few ms, should not be loading 
     // This doesn't seem to be the problem 
     setTimeout(() => { 
      _fixture.detectChanges(); 
      expect(_fixture.nativeElement.querySelector('.loader')).toBeFalsy(); 
      cb(); 
     }, 10); 
    }); 
}); 

Это было прекрасно работать в Angular2 rc1 и выдает ошибку в rc4, любые предложения?

Кроме того, нет никаких ошибок, если вы используете setTimeout непосредственно из самого теста

 fit('lets you run timeouts', async(() => { 
      setTimeout(() => { 
       expect(1).toBe(1); 
      }, 10); 
     })); 
+0

Можете ли вы использовать настройку Jasmine done()? - https://stackoverflow.com/questions/38697656/testing-angular2-components-that-use-setinterval-or-settimeout/40688469#40688469 – spinkus

+0

@ S.Pinkus Я, см. функцию cb? –

+0

О да, мое плохое. – spinkus

ответ

2

Я столкнулся с той же проблемой. И я смог обходить его, используя параметр жасмина done.

fit('lets you run timeouts', (done) => { 
    async(() => { 
     setTimeout(() => { 
      expect(1).toBe(1); 
      done(); 
     }, 10); 
    }); 
}); 
+0

Спасибо, но пример, который вы скопировали, не вызывает проблемы для меня. –

+0

Кроме того, добавление обратного вызова к первому примеру в моем вопросе не решает проблему. Вот как я на самом деле имел это раньше, прежде чем я узнал, как работает async. –

3

Я обнаружил, что по какой-то причине, вы не можете использовать обещание, созданный с Observable.of(anything).delay() в тесте.

Мое решение для этого заключалось в том, чтобы реализовать эту линию самостоятельно, что почти разумно, учитывая, что другой пример, опубликованный в вопросе, действительно сработал.

// This is what we should be doing, but somehow, it isn't working. 
// return Observable.of(result).delay(0)); 
function createDelayedObservable <T>(result:any, time:number = 0):Observable<T> { 
    return new Observable<T>(observer => { 
     setTimeout(() => observer.next(result), time); 
    }); 
} 

Однако, я до сих пор не понимаю, почему следующее не провалится, так что я не принимает мой собственный ответ в надежде, что кто-то с твердым пониманием зон может сказать мне, что происходит.

it('should be able to use delay in tests', (cb) => { 
    var obs = Observable.of(1).delay(0); 
    obs.subscribe(val => { 
     expect(val).toBe(1); 
     cb() 
    }); 
}); 
+0

У меня есть аналогичная проблема, я хочу протестировать компонент, содержащий элемент this.scroll = Observable.fromEvent (document.querySelector ('nav'), 'scroll'). DebounceTime (100) .subscribe ((event) => {this.check();}); ' , но поскольку это компонент, я не знаю, где бы я разместил обратный вызов – select