Lettable演算子の前に、debounceTimeメソッドを変更するヘルパーを実行したので、TestSchedulerを使用します。
export function mockDebounceTime(
scheduler: TestScheduler,
overrideTime: number,
): void {
const originalDebounce = Observable.prototype.debounceTime;
spyOn(Observable.prototype, 'debounceTime').and.callFake(function(
time: number,
): void {
return originalDebounce.call(
this,
overrideTime,
scheduler,
);
});
}
したがって、次のObservableのテストは簡単でした。
@Effect()
public filterUpdated$ = this.actions$
.ofType(UPDATE_FILTERS)
.debounceTime(DEFAULT_DEBOUNCE_TIME)
.mergeMap(action => [...])
Lettable演算子を使用すると、filterUpdated $ Observableは次のように記述されます。
@Effect()
public filterUpdated$ = this.actions$
.ofType(UPDATE_FILTERS)
.pipe(
debounceTime(DEFAULT_DEBOUNCE_TIME),
mergeMap(action => [...])
);
DebounceTime演算子にパッチを適用できなくなりました! testSchedulerをdebounceTimeオペレーターに渡すにはどうすればよいですか?
カスタムスケジューラを受け入れる2番目の引数を使用できます。
debounceTime(DEFAULT_DEBOUNCE_TIME, rxTestScheduler),
すべてのコード
import { Scheduler } from 'rxjs/scheduler/Scheduler';
import { asap } from 'rxjs/scheduler/asap';
@Injectable()
export class EffectsService {
constructor(private scheduler: Scheduler = asap) { }
@Effect()
public filterUpdated$ = this.actions$
.ofType(UPDATE_FILTERS)
.pipe(
debounceTime(DEFAULT_DEBOUNCE_TIME, this.scheduler),
mergeMap(action => [...])
);
}
その後、テストで
describe('Service: EffectsService', () => {
//setup
beforeEach(() => TestBed.configureTestingModule({
EffectsService,
{ provide: Scheduler, useValue: rxTestScheduler} ]
}));
//specs
it('should update filters using debounce', inject([EffectsService], service => {
// your test
});
});
.pipe()
はまだObservableプロトタイプ上にあるため、モック手法を使用できます。
Lettable演算子(おっと、今ではそれらを呼び出すことになっています pipeable演算子 )は、モックパイプ内でそのまま使用できます。
これは、クリーンなCLIアプリケーションのapp.component.spec.tsで使用したコードです。 TestSchedulerの最善の使用法ではないかもしれませんが、原則を示していることに注意してください。
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { Observable } from 'rxjs/Observable';
import { debounceTime, take, tap } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/Rx';
export function mockPipe(...mockArgs) {
const originalPipe = Observable.prototype.pipe;
spyOn(Observable.prototype, 'pipe').and.callFake(function(...actualArgs) {
const args = [...actualArgs];
mockArgs.forEach((mockArg, index) => {
if(mockArg) {
args[index] = mockArg;
}
});
return originalPipe.call(this, ...args);
});
}
describe('AppComponent', () => {
it('should test lettable operators', () => {
const scheduler = new TestScheduler(null);
// Leave first tap() as-is but mock debounceTime()
mockPipe(null, debounceTime(300, scheduler));
const sut = Observable.timer(0, 300).take(10)
.pipe(
tap(x => console.log('before ', x)),
debounceTime(300),
tap(x => console.log('after ', x)),
take(4),
);
sut.subscribe((data) => console.log(data));
scheduler.flush();
});
});
TestScheduler
インスタンスを演算子に挿入または渡すことが難しい場合、この最も簡単な解決策は、now
インスタンスのschedule
メソッドとAsyncScheduler
メソッドをに再バインドすることです。 TestScheduler
インスタンスのもの。
これは手動で行うことができます。
import { async } from "rxjs/Scheduler/async";
it("should rebind to the test scheduler", () => {
const testScheduler = new TestScheduler();
async.now = () => testScheduler.now();
async.schedule = (work, delay, state) => testScheduler.schedule(work, delay, state);
// test something
delete async.now;
delete async.schedule;
});
または、sinon
スタブを使用できます。
import { async } from "rxjs/Scheduler/async";
import * as sinon from "sinon";
it("should rebind to the test scheduler", () => {
const testScheduler = new TestScheduler();
const stubNow = sinon.stub(async, "now").callsFake(
() => testScheduler.now()
);
const stubSchedule = sinon.stub(async, "schedule").callsFake(
(work, delay, state) => testScheduler.schedule(work, delay, state)
);
// test something
stubNow.restore();
stubSchedule.restore();
});
更新:アクションの配列を返し、それらすべてを検証する場合は、
.pipe(throttleTime(1, myScheduler))
また、独自のスケジューラを作成する代わりに、jasmine-marblesからgetTestSchedulerを使用できます。
import { getTestScheduler } from 'jasmine-marbles';
したがって、テストは次のようになります。
it('should pass', () => {
getTestScheduler().run((helpers) => {
const action = new fromAppActions.LoadApps();
const completion1 = new fromAppActions.FetchData();
const completion2 = new fromAppActions.ShowWelcome();
actions$ = helpers.hot('-a', { a: action });
helpers
.expectObservable(effects.load$)
.toBe('300ms -(bc)', { b: completion1, c: completion2 });
});
});
DebounceTimeでngrx効果をテストするのに苦労していました。今は少し変わったようです。私はここのドキュメントに従いました: https://github.com/ReactiveX/rxjs/blob/master/doc/marble-testing.md
これが私のテストの様子です:
describe('someEffect$', () => {
const myScheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
it('should test', () => {
myScheduler.run((helpers) => {
const action = new fromActions.SomeAction();
const completion = new fromActions.SomeCompleteAction(someData);
actions$.stream = helpers.hot('-a', { a: action });
helpers
.expectObservable(effects.someEffect$.pipe(throttleTime(1, myScheduler)))
.toBe('200ms -(b)', { b: completion });
});
});
});
実際のコードでスケジューラーを使用する必要はありません。例:no debounceTime(200、this.scheduler)
上記の回答にいくつか問題がありました(Observable.prototypeに複数のスパイを含めることはできません...)、私に関連するのは「debounceTime」をモックするだけだったので、実際のdebounceTime(たとえばfilterTextDebounceTime = 200)をの変数に移動しましたコンポーネントと仕様の「beforeEach」で、component.filterTextDebounceTimeを0に設定しているので、debounceTimeは同期/ブロッキングで機能しています。