我试图在Ionic中为我的ngrx效果编写一些测试,但无法让它工作。在线有几个教程,但它们不适用于我的案例或不适用。 official ngrx migration guide中的示例,例如不再起作用了。
我想测试loadAllAction $效果处理LoadAllAction的情况,最后调度LoadAllCompleteAction。
这是我的代码。我将类和服务重命名为更清晰,所以如果有拼写错误请忽略它。实际代码中的导入也都是正确的。
myEffects.effects.ts
import {Injectable} from "@angular/core";
import {Actions, Effect} from "@ngrx/effects";
import {catchError, concat, map, switchMap} from "rxjs/operators";
import {Observable} from "rxjs/Observable";
import {IMyObject} from "../models/myObject.model";
import {MyObjectService} from "../../services/myObject.service";
import {
InitMyObjectsCompleteAction,
LOAD_ALL,
LOAD_ALL_COMPLETE,
LoadAllAction,
LoadAllCompleteAction,
SaveMyObjectsCompleteAction, MyObjectsErrorAction
} from "../actions/object.actions";
import {defer} from "rxjs/observable/defer";
import {LogService} from "../../services/log/log.service";
import {ToastService} from "../../services/toast.service";
@Injectable()
export class MyEffects {
constructor(private action$: Actions,
private myObjectService: MyObjectService,
private logService: LogService,
private toastService: ToastService) {
}
@Effect() init$ = defer(() => Observable.fromPromise(this.myObjectService.getSavedMyObjects())
.pipe(
map((favoredObjects: IMyObject[]) => new InitMyObjectsCompleteAction(favoredObjects)),
catchError((error) => this.createErrorObservableAndLog(error)),
concat(Observable.of(new LoadAllAction()))
));
@Effect() loadAllAction$ = this.action$.ofType(LOAD_ALL).pipe(
switchMap(() => this.myObjectService.loadObjects().pipe(
map((favoredObjects: IMyObject[]) => new LoadAllCompleteAction(favoredObjects)),
catchError((error) => {
this.toastService.couldNotUpdateObject();
return this.createErrorObservableAndLog(error)
})
))
);
@Effect() loadAllCompleteAction$ = this.action$.ofType(LOAD_ALL_COMPLETE).pipe(
switchMap((action: LoadAllCompleteAction) => Observable.fromPromise(this.myObjectService.saveObjects(action.favoredObjects))
.pipe(
map(() => new SaveMyObjectsCompleteAction()),
catchError((error) => this.createErrorObservableAndLog(error))
)
)
);
private createErrorObservableAndLog(error){
this.logService.error(error);
return Observable.of(new MyObjectsErrorAction(error))
}
}
mocks.ts
export class MyObjectsServiceMock {
loadMyObjects(): Observable<MyObjects[]> {
return createDummyMyObjectsListObservable();
}
}
export class StorageMock {
get() {
return [];
}
set() {
}
}
export class StoreMock {
private returnMyObjects: boolean = false;
public dispatch() {
return Observable.empty();
}
public select() {
if (this.returnMyObjects)
return createDummyMyObjectsListObservable();
return Observable.empty();
}
public setReturnObjects(wantMyObjects: boolean) {
this.returnMyObjects = wantMyObjects;
}
}
这是迄今为止的测试。无论我自己尝试什么教程,结合其他人 - 它根本不起作用,而且我很难找到从哪里开始。在下面的示例中,操作不具有“下一个”属性&#39;因为它是一个可观察的,尽管我从迁移指南中直接复制了它。 &#39;中的expect()。toBe()也应该工作&#39; - 测试也不正确 - 但我找不到正确语法的文档。
myEffects.effects.spec.ts
import {TestBed} from "@angular/core/testing";
import {MyObjectServiceMock, StorageMock, StoreMock} from "../../../test-config/mocks-ionic";
import {Store} from "@ngrx/store";
import {LogService} from "../../services/log/log.service";
import {MyEffects} from "./myEffects.effects";
import {MyObjectService} from "../../services/myObject.service";
import {provideMockActions} from "@ngrx/effects/testing";
import {Observable} from "rxjs/Observable";
import {cold, hot} from 'jasmine-marbles';
import {InitMyObjectsCompleteAction, LoadAllAction, LoadAllCompleteAction} from "../actions/myObject.actions";
import {Storage} from "@ionic/storage";
import {ToastService} from "../../services/toast.service";
import {ToastController} from "ionic-angular";
import {ReplaySubject} from "rxjs/ReplaySubject";
describe('MyObject Effects', () => {
let myObject: MyObjectEffects;
let actions: Observable<any>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MyObjectEffects,
provideMockActions(() => actions),
ToastService,
ToastController,
LogService,
{provide: Store, useClass: StoreMock},
{provide: MyObjectService, useClass: MyObjectServiceMock},
{provide: Storage, useClass: StorageMock}
]
})
myObjectEffects = TestBed.get(MyObjectEffects);
});
it('should work', () => {
actions = hot('--a-', {a: InitMyObjectsCompleteAction});
const expected = cold('--b', {b: LoadAllCompleteAction});
expect(myObjectEffects.loadAllCompleteAction$).toBeObservable(expected);
});
it('should work also', () => {
actions = new ReplaySubject(1);
actions.next(new LoadAllAction());
myObjectEffects.loadAllCompleteAction$.subscribe(result => {
expect(result).toBe(new LoadAllCompleteAction());
});
});
});
基本上归结为2个问题:
1。如何以正确的方式配置TestBed,我甚至必须这样做 嘲笑一切?
2。我如何正确地测试正确的过程 如上所述和代码中的调度操作?
我显然没有要求你为我写测试 - 但也许你知道我可以在哪里开始或指出我正确的方向。非常感谢,我真的很感激!
答案 0 :(得分:0)
想出来,也许是将来帮助某人的解决方案。
在beforeEach()
中从TestBed中注入效果和模拟服务 service = TestBed.get(MyObjectService);
myObjecEffects = TestBed.get(MyObjectEffects);
将操作模拟为Type Action的ReplaySubject:
provideMockActions(() => actions)
订阅我想在此过程中测试的每个效果:
myObjectEffects.loadAllAction$.subscribe((result) => {})
在您要测试的任何电话或其他内容的每个订阅测试中,并为操作主题提供新值:
myObjectEffects.loadAllAction$.subscribe((result) => actions.next(result))
。
对要测试的每个效果重复此操作。
由于这可能不是很清楚,所以这是整个测试:
describe('MyObject Effects', () => {
let myObjectEffects: MyObjectEffects;
let actions: ReplaySubject<Action>;
let service: MyObjectService;
beforeEach(() => {
actions = new ReplaySubject(1);
TestBed.configureTestingModule({
declarations: [MyObjectsApp],
imports: [
IonicModule.forRoot(MyObjectsApp)
],
providers: [
MyObjectEffects,
provideMockActions(() => actions),
ToastService,
ToastController,
LogService,
{provide: MyObjectService, useClass: MyObjectServiceMock},
{provide: Store, useClass: StoreMock}
]
});
service = TestBed.get(MyObjectService);
myObjectEffects = TestBed.get(MyObjectEffects);
});
it('loadAll action should eventually save objects', () => {
actions.next(new LoadAllAction());
spyOn(service, 'loadObjects').and.returnValue(Observable.of([new Object({id: 20})]));
myObjectEffects.loadAllAction$.subscribe((result) => {
expect(service.loadObjects).toHaveBeenCalledTimes(1);
expect(result).toEqual(new LoadAllCompleteAction([new Object({id: 20})]));
actions.next(result);
});
objectEffects.loadAllCompleteAction$.subscribe((result) => {
expect(result).toEqual(new SaveObjectsCompleteAction());
});
})
});