Jest and Jasmine examples found in this repo
Run npm install and npm test in each folder. The test is found in src/app/example.spec.ts
Invoking the async callback in a Jasmine or Jest test does not appear to work if you do it inside the subscribe block of an effect.
E.g. in the code below
import { Action } from '@ngrx/store';
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { map } from 'rxjs/operators';
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Observable } from 'rxjs';
import { hot } from 'jasmine-marbles';
const EXAMPLE = 'Example';
class ExampleAction implements Action {
readonly type = EXAMPLE;
constructor() { }
}
@Injectable()
class ExampleEffects {
constructor(private actions$: Actions) { }
@Effect()
go = this.actions$.pipe(
ofType(EXAMPLE),
map(() => true),
);
}
describe('Example', () => {
let effects: ExampleEffects;
let actions: Observable<any>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
],
providers: [
ExampleEffects,
provideMockActions(() => actions),
],
});
effects = TestBed.get(ExampleEffects);
});
it('it should work', (done) => {
actions = hot('-a-|', { a: new ExampleAction() });
effects.go.subscribe(() => {
console.log('before done');
done();
console.log('after done');
});
});
});
For jasmine/karma we see the following output:
LOG: 'before done'
HeadlessChrome 72.0.3626 (Windows 10.0.0): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
LOG: 'after done'
HeadlessChrome 72.0.3626 (Windows 10.0.0): Executed 0 of 1 SUCCESS (0 secs / 0 secs)
HeadlessChrome 72.0.3626 (Windows 10.0.0) Example it should work FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
HeadlessChrome 72.0.3626 (Windows 10.0.0): Executed 1 of 1 (1 FAILED) (0 secs / 5.035 secs)
and for Jest we see:
● Example › it should work
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
41 | });
42 |
> 43 | it.only('it should work', (done) => {
| ^
44 | actions = hot('-a-|', { a: new ExampleAction() });
45 |
46 | effects.go.subscribe(() => {
at Spec (node_modules/jest-jasmine2/build/jasmine/Spec.js:85:20)
at src/app/example.spec.ts:43:8
at Object.<anonymous> (src/app/example.spec.ts:26:1)
console.log src/app/example.spec.ts:47
before done
console.log src/app/example.spec.ts:49
after done
In both cases after done is logged to the console so done() should have been called.
The above test should pass.
NgRx 7.2.0
rxjs 6.3.3
Angular 7.2.0
This appears to be a problem with effects specifically, regular observables behave as expected - the following test works
it('it should work', (done) => {
of({}).subscribe(() => {
console.log('before done');
done();
console.log('after done');
});
});
[ ] Yes (Assistance is provided if you need help submitting a pull request)
[X ] No
I am seeing this behavior as well
I believe this is because you're using the marbles syntax, the following does work.
actions = new ReplaySubject(1);
actions.next(new ExampleAction());
effects.go.subscribe(x => {
done();
});
I'm not 100% sure, but I think if you're using the marble syntax you should also use .toBeObservable() to test the stream - expect(effects.someSource$).toBeObservable(expected);.
See the docs for more info.
This is because jasmine-marbles is using the TestScheduler (which will create a queue of actions) , to fire the actions you'll have to flush the actions:
actions = hot('-a--|', { a: new ExampleAction() });
const scheduler = getTestScheduler();
effects.go.subscribe(() => {
console.log('beforeX done');
done();
console.log('after done');
});
scheduler.flush();
The RxJS version of this would be the following:
actions = of(new ExampleAction(), getTestScheduler());
const scheduler = getTestScheduler();
effects.go.subscribe(() => {
console.log('beforeX done');
done();
console.log('after done');
});
scheduler.flush();
Most helpful comment
This is because
jasmine-marblesis using theTestScheduler(which will create a queue of actions) , to fire the actions you'll have toflushthe actions:The RxJS version of this would be the following: