I am trying to test a ngrx effects in Angular 6 project, I always get error:
Expected $[0].notification.kind = 'C' to equal 'N'.
Expected $[0].notification.hasValue = false to equal true.
I tried this post https://brianflove.com/2018-06-28/ngrx-testing-effects and the one in the ngrx doc. Is there any requirements to make test on effects with ngrx 6 ? The error is not meaningful enough for me. Maybe someone have a complete example about how to do ?
Here's my effect:
initData$: Observable<Action> = this.actions$.pipe(
ofType(INIT_DATA_ACTION),
switchMap((data: any) => {
return this.store.pipe(select(getAppDataResolved)).take(1).switchMap((resolved: any) => {
if (!resolved) {
return this.dataService.getInitData(this.loginService.user.id).switchMap((response: any) => {
return Observable.from([
new ItemsInitDataAction(response.userItems),
new InitDataResolvedAction(),
]);
});
} else {
return Observable.from([
new InitDataResolvedAction(),
]);
}
});
}),
);
and my karma test:
it('should be created', () => {
expect(effects).toBeTruthy(); // got success
});
it('basic test', () => { // got error
const action = new appAction.InitDataAction();
const outcome = new appAction.InitDataResolvedAction();
actions.stream = hot('a', { a: action });
const expected = hot('a', { b: outcome });
expect(effects.initData$).toBeObservable(expected);
});
});
Thanks in advance for helping ;-)
I think you need to insert a mock for the Selector, the next line in the console should be describe something about missing selector data.
let store: Store<any>
class MockStore {
select(){}
}
TestBed.configureTestingModule({
providers: [
{
provide: Store,
useClass: MockStore
}
]
});
store = TestBed.get(Store);
And in test suite you can use Spy to give you any slice of store that you want:
spyOn(store, 'select').and.returnValue(of(initialState));
There is a Typo in expected. It should be 'b' instead of 'a'
const expected = hot('b', { b: outcome });
I havent been able to get a testing working with marbles yet.
Im using Nrwl nx, so my effects test looks like this:
import { TestBed } from '#angular/core/testing';
import { Subject, ReplaySubject } from 'rxjs';
import { EffectsModule } from '#ngrx/effects';
import { StoreModule } from '#ngrx/store';
import { provideMockActions } from '#ngrx/effects/testing';
import { NxModule } from '#nrwl/nx';
import { DataPersistence } from '#nrwl/nx';
import { ChangePasswordEffects } from './change-password.effects';
import { ChangePassword, ChangePasswordSuccessful } from './change-password.actions';
import { HttpClientTestingModule } from '#angular/common/http/testing';
describe('ChangePasswordEffects', () => {
let actions: Subject<any>;
let effects: ChangePasswordEffects;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NxModule.forRoot(), StoreModule.forRoot({}), EffectsModule.forRoot([]), HttpClientTestingModule],
providers: [ChangePasswordEffects, DataPersistence, provideMockActions(() => actions)]
});
effects = TestBed.get(ChangePasswordEffects);
});
describe('loadChangePassword$', () => {
it('should work', () => {
actions = new ReplaySubject(1);
actions.next(ChangePassword);
effects.loadChangePassword$.subscribe(result => {
expect(result).toEqual(ChangePasswordSuccessful);
});
});
});
});
And my code looks like this:
import { PasswordChangeError } from './../../models/password-change-error';
import { Injectable } from '#angular/core';
import { Effect, Actions } from '#ngrx/effects';
import { DataPersistence } from '#nrwl/nx';
import { ChangePasswordPartialState } from './change-password.reducer';
import {
ChangePassword,
ChangePasswordSuccessful,
ChangePasswordError,
ChangePasswordActionTypes
} from './change-password.actions';
import { ChangePasswordService } from '../../services/change-password/change-password.service';
import { map } from 'rxjs/operators';
#Injectable()
export class ChangePasswordEffects {
#Effect() loadChangePassword$ = this.dataPersistence.fetch(ChangePasswordActionTypes.ChangePassword, {
run: (action: ChangePassword, state: ChangePasswordPartialState) => {
return this.passwordService
.changePassword(action.newPassword, action.userId)
.pipe(map(res => new ChangePasswordSuccessful(res)));
},
onError: (action: ChangePassword, error: PasswordChangeError) => {
return new ChangePasswordError(error);
}
});
constructor(
private actions$: Actions,
private dataPersistence: DataPersistence<ChangePasswordPartialState>,
private passwordService: ChangePasswordService
) {}
}
Related
I made unit test for sendFCM in push.service.spec.ts. but it's not working on the nest server.
Issue #1 : How can I normally give the return value of sendmulticast method?
Property 'success' does not exist on type 'void' below 'success' code.
```expect((await response).success).toBe(true);```
Issue #2 : I looked at many similar issue solutions, but couldn't find a solution to the below error. How can I test normally?
Nest can't resolve dependencies of the PushService (FIREBASE_ADMIN_INJECT, ?). Please make sure that the argument HttpService at index [1] is available in the RootTestModule
[Error Message]
FAIL push/push.service.spec.ts (24.852 s)
● PushService › should be defined
Nest can't resolve dependencies of the PushService (FIREBASE_ADMIN_INJECT, ?). Please make sure that the argument HttpService at index [1] is available in the RootTestModule context.
Potential solutions:
- If HttpService is a provider, is it part of the current RootTestModule?
- If HttpService is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing HttpService */ ]
})
at TestingInjector.lookupComponentInParentModules (../node_modules/#nestjs/core/injector/injector.js:231:19)
at TestingInjector.resolveComponentInstance (../node_modules/#nestjs/core/injector/injector.js:184:33)
at TestingInjector.resolveComponentInstance (../node_modules/#nestjs/testing/testing-injector.js:16:45)
at resolveParam (../node_modules/#nestjs/core/injector/injector.js:106:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../node_modules/#nestjs/core/injector/injector.js:121:27)
at TestingInjector.loadInstance (../node_modules/#nestjs/core/injector/injector.js:52:9)
at TestingInjector.loadProvider (../node_modules/#nestjs/core/injector/injector.js:74:9)
at async Promise.all (index 3)
● PushService › sendFCM › should return an array of Semesters
Nest can't resolve dependencies of the PushService (FIREBASE_ADMIN_INJECT, ?). Please make sure that the argument HttpService at index [1] is available in the RootTestModule context.
Potential solutions:
- If HttpService is a provider, is it part of the current RootTestModule?
- If HttpService is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing HttpService */ ]
})
at TestingInjector.lookupComponentInParentModules (../node_modules/#nestjs/core/injector/injector.js:231:19)
at TestingInjector.resolveComponentInstance (../node_modules/#nestjs/core/injector/injector.js:184:33)
at TestingInjector.resolveComponentInstance (../node_modules/#nestjs/testing/testing-injector.js:16:45)
at resolveParam (../node_modules/#nestjs/core/injector/injector.js:106:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../node_modules/#nestjs/core/injector/injector.js:121:27)
at TestingInjector.loadInstance (../node_modules/#nestjs/core/injector/injector.js:52:9)
at TestingInjector.loadProvider (../node_modules/#nestjs/core/injector/injector.js:74:9)
at async Promise.all (index 3)
[push.service.spec.ts]
import { Test, TestingModule } from '#nestjs/testing';
import { FirebaseAdminModule } from '#tfarras/nestjs-firebase-admin';
import { SendFcmReqDto } from './dtos/send-fcm.dto';
import { PushService } from './push.service';
import * as admin from 'firebase-admin';
describe('PushService', () => {
let service: PushService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
FirebaseAdminModule.forRootAsync({
useFactory: () => ({
credential: admin.credential.applicationDefault()
})
}),
],
providers: [PushService],
}).compile();
service = module.get<PushService>(PushService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('sendFCM', () => {
const sendFcmReqDto: SendFcmReqDto = {
notification_type: 'type_1',
subject: 'FCM Push Unit Test',
content: 'type1',
small_icon_url: '',
large_icon_url: '',
action1: '',
action2: '',
big_pic_url: '',
is_expand: 'true',
is_sicon: 'false',
is_licon: 'false'
}
it('should return an array of Semesters', async () => {
`const response = service.sendFCM(sendFcmReqDto);`
expect((await response).success).toBe(true);
})
})
});
[push.module.ts]
import { Module } from '#nestjs/common';
import { PushController } from './push.controller';
import { PushService } from './push.service';
import { HttpModule } from '#nestjs/axios';
import { FirebaseAdminModule } from '#tfarras/nestjs-firebase-admin';
import * as admin from 'firebase-admin';
#Module({
imports: [
HttpModule.register({
timeout: 5000,
maxRedirects: 5,
}),
FirebaseAdminModule.forRootAsync({
useFactory: () => ({
credential: admin.credential.applicationDefault()
})
}),
],
controllers: [PushController],
exports: [PushService],
providers: [PushService],
})
export class PushModule {}
import { Module } from '#nestjs/common';
import { PushController } from './push.controller';
import { PushService } from './push.service';
import { HttpModule } from '#nestjs/axios';
import { FirebaseAdminModule } from '#tfarras/nestjs-firebase-admin';
import * as admin from 'firebase-admin';
#Module({
imports: [
HttpModule.register({
timeout: 5000,
maxRedirects: 5,
}),
FirebaseAdminModule.forRootAsync({
useFactory: () => ({
credential: admin.credential.applicationDefault()
})
}),
],
controllers: [PushController],
exports: [PushService],
providers: [PushService],
})
export class PushModule {}
[push.controller.ts]
import { Body, Controller, Post } from '#nestjs/common';
import { PushService } from './push.service';
import { SendFcmReqDto } from './dtos/send-fcm.dto';
#Controller('firebase')
#Controller({ path: 'push', version: '10' })
export class PushController {
constructor(private pushService: PushService) {}
#Post('push')
sendFCM(#Body() fcmReq: SendFcmReqDto) {
return this.pushService.sendFCM(fcmReq);
}
}
[push.service.ts]
import {
BadRequestException,
Injectable,
Inject,
Logger,
NotImplementedException,
} from '#nestjs/common';
import { HttpService } from '#nestjs/axios';
import { resourceLimits } from 'worker_threads';
import { FIREBASE_ADMIN_INJECT, FirebaseAdminSDK } from '#tfarras/nestjs-firebase-admin';
import { SendFcmReqDto } from './dtos/send-fcm.dto';
import { User } from 'src/entities/User';
import { Repository } from 'typeorm';
import { InjectRepository } from '#nestjs/typeorm';
#Injectable()
export class PushService {
[x: string]: any;
private readonly logger = new Logger('PushService');
constructor(
#Inject(FIREBASE_ADMIN_INJECT)
private firebaseAdmin: FirebaseAdminSDK,
private readonly httpService: HttpService,
) {}
async sendFCM({
notification_type,
subject,
content,
small_icon_url,
large_icon_url,
action1,
action2,
big_pic_url,
is_expand,
is_sicon,
is_licon
}: SendFcmReqDto) {
this.logger.log('Start sendFCM()!');
const users = await this.usersRepository.find(
{select: ['fbToken']},
);
const ids: string[] = [];
users.forEach ((user) => {
ids.push(user.fbToken);
});
// const aps
const apsPayload = {
sound:'default',
contentAvailable:1,
alert:'default'
};
const dataPayload = {
noti_type: notification_type,
title: subject,
body: content,
s_icon_url: small_icon_url,
l_icon_url: large_icon_url,
act1: action1,
act2: action2,
b_pic_url: big_pic_url,
is_expand: is_expand,
is_sicon: is_sicon,
is_licon: is_licon
}
const multicast_message = {
tokens: ids,
priority:'high',
aps:apsPayload,
data:dataPayload,
};
await this.firebaseAdmin
.messaging()
.sendMulticast(multicast_message)
.then((response)=> {
this.logger.log('send_response:' + response);
return {
success: true,
result: response,
};
})
.catch((error) => {
this.logger.log('send_response:' + error);
return {
success: false,
result: error,
};
})
}
}
I am new to Vue and followed the recommendation to use vue testing library. The only issue is I can't seem to find a way to inject my code into globalProperties in render function.
Does anyone know of an example where I can inject or mock it out?
main.js
app.config.globalProperties.$globals = globalMethods
...
const app = createApp(App)
app.config.globalProperties.$globals = globalMethods
app.config.globalProperties.$globalVars = globalVars
app.component("font-awesome-icon", fontawesome)
app.use(applicationStore);
app.use (Hotjar, hotjarConfig)
app.use(i18n)
app.use(router)
app.mount('#app')
From my vue component in create I am able to call
Component.vue
let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
...
,
created() {
let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
this.myInputValue = formatedObj.formatedString;
this.formatedCharacterCount = formatedObj.formatedCharacterCount;
this.prevValue = this.myInputValue;
},
...
tesst.spec.js
import { render } from '#testing-library/vue'
import FormatedNumericInput from '#/components/Component.vue'
import {globalMethods} from'#/config/global-methods'
const label = 'Price'
const initSettings = {
props: {
inputId: 'testInputId1',
labelTxt: label
}
};
beforeEach(() => {
});
test('a simple string that defines your test', () => {
const { getByLabelText } = render(FormatedNumericInput, initSettings)
const input = getByLabelText(label)
// testing logic
expect(input != null).toBe(true)
expect(FormatedNumericInput != null).toBe(true)
})
** ERROR **
TypeError: Cannot read property 'maskValues' of undefined
85 | },
86 | created() {
> 87 | let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
| ^
88 | this.myInputValue = formatedObj.formatedString;
89 | this.formatedCharacterCount = formatedObj.formatedCharacterCount;
90 | this.prevValue = this.myInputValue;
at Proxy.created (src/components/FormatedNumericInput.vue:87:37)
The second argument of render() is passed to #vue/test-utils mount(), so you could include the global.mocks mounting option to mock $globals.maskValues:
const { getByLabelText } = render(FormatedNumericInput, {
...initSettings,
global: {
mocks: {
$globals: {
maskValues: (inputValue, inputType) => {
const formatedString = globalFormatValue(inputValue) // declared elsewhere
return {
formatedString,
formatedCharacterCount: formatedString.length,
}
}
}
}
}
})
This is my solution in actual Vue3/Vite/Vitest environment, I set some mocks globally, so I don't need to in every test suite.
// vitest.config.ts
import { mergeConfig } from 'vite';
import { defineConfig } from 'vitest/config';
import viteConfig from './vite.config';
export default defineConfig(
mergeConfig(viteConfig, { // extending app vite config
test: {
setupFiles: ['tests/unit.setup.ts'],
environment: 'jsdom',
}
})
);
// tests/unit.setup.ts
import { config } from "#vue/test-utils"
config.global.mocks = {
$t: tKey => tKey; // just return translation key
};
so for you it will be something like
config.global.mocks = {
$globals: {
maskValues: (inputValue, inputType) => {
// ...implementation
return {
formatedString,
formatedCharacterCount,
}
}
}
}
I'm using vue2 with composition Api, vuex and apollo client to request a graphql API and I have problems when mocking composable functions with jest
// store-service.ts
export function apolloQueryService(): {
// do some graphql stuff
return { result, loading, error };
}
// store-module.ts
import { apolloQueryService } from 'store-service'
export StoreModule {
state: ()=> ({
result: {}
}),
actions: {
fetchData({commit}) {
const { result, loading, error } = apolloQueryService()
commit('setState', result);
}
},
mutations: {
setState(state, result): {
state.result = result
}
}
}
The Test:
// store-module.spec.ts
import { StoreModule } from store-module.ts
const store = StoreModule
describe('store-module.ts', () => {
beforeEach(() => {
jest.mock('store-service', () => ({
apolloQueryService: jest.fn().mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
})
}))
})
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
The test fails, because the commit gets called with ('setData', { value: undefined }) which is the result from the original apolloQueryService. My Mock doesn't seem to work. Am I doing something wrong? Appreciate any help, thanks!
Try this :
// store-module.spec.ts
import { StoreModule } from store-module.ts
// first mock the module. use the absolute path to store-service.ts from the project root
jest.mock('store-service');
// then you import the mocked module.
import { apolloQueryService } from 'store-service';
// finally, you add the mock return values for the mock module
apolloQueryService.mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
});
/* if the import order above creates a problem for you,
you can extract the first step (jest.mock) to an external setup file.
You should do this if you are supposed to mock it in all tests anyway.
https://jestjs.io/docs/configuration#setupfiles-array */
const store = StoreModule
describe('store-module.ts', () => {
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
I am trying to make Jasmine & Karma framework into the current angular application running in ver 8.2. But i am coming across this weird error inside the Karma test running window:
Failed: Unexpected value 'DecoratorFactory' imported by the module 'DynamicTestModule'. Please add a #NgModule annotation.
What is the problem?
My componenent.spec.ts looks like this:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { HomeComponent } from './home.component';
import { NO_ERRORS_SCHEMA} from '#angular/core';
import {RouterTestingModule} from '#angular/router/testing';
import {HttpClientTestingModule} from '#angular/common/http/testing';
import { MsalService } from '#azure/msal-angular';
import { Store } from '#ngrx/store';
import { Pipe } from '#angular/core';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule, HttpClientTestingModule, Pipe]
,declarations: [HomeComponent]
,schemas:[NO_ERRORS_SCHEMA]
,providers: [
{provide: MsalService, useFactory: '' },
{provide: Store, useFactory: '' }
]
})
.compileComponents();
}));
it('should have header text', async(() => {
const fixture = TestBed.createComponent(HomeComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
//expect(compiled.querySelector('.header-txt').textContent).toContain('Tax');
}));
});
I found the cause :-
export class MockStore<T> {
private state: BehaviorSubject<T> = new BehaviorSubject(undefined);
setState(data: T) { this.state.next(data); }
select(selector?: any): Observable<T> {
return this.state.asObservable();
}
pipe() {}
dispatch(action: any) { }
}
========================================================================
TestBed.configureTestingModule({
{provide: Store, useFactory: 'MockStore' }
..............
The useFactory property must be some custom class name. Now i mocked the store class.
There are plenty of tutorials how to test effects in ngrx 3.
However, I've found only 1 or 2 for ngrx4 (where they removed the classical approach via EffectsTestingModule ), e.g. the official tutorial
However, in my case their approach doesn't work.
effects.spec.ts (under src/modules/list/store/list in the link below)
describe('addItem$', () => {
it('should return LoadItemsSuccess action for each item', async() => {
const item = makeItem(Faker.random.word);
actions = hot('--a-', { a: new AddItem({ item })});
const expected = cold('--b', { b: new AddUpdateItemSuccess({ item }) });
// comparing marbles
expect(effects.addItem$).toBeObservable(expected);
});
})
effects.ts (under src/modules/list/store/list in the link below)
...
#Effect() addItem$ = this._actions$
.ofType(ADD_ITEM)
.map<AddItem, {item: Item}>(action => {
return action.payload
})
.mergeMap<{item: Item}, Observable<Item>>(payload => {
return Observable.fromPromise(this._listService.add(payload.item))
})
.map<any, AddUpdateItemSuccess>(item => {
return new AddUpdateItemSuccess({
item,
})
});
...
Error
should return LoadItemsSuccess action for each item
Expected $.length = 0 to equal 1.
Expected $[0] = undefined to equal Object({ frame: 20, notification: Notification({ kind: 'N', value: AddUpdateItemSuccess({ payload: Object({ item: Object({ title: Function }) }), type: 'ADD_UPDATE_ITEM_SUCCESS' }), error: undefined, hasValue: true }) }).
at compare (webpack:///node_modules/jasmine-marbles/index.js:82:0 <- karma-test-shim.js:159059:33)
at Object.<anonymous> (webpack:///src/modules/list/store/list/effects.spec.ts:58:31 <- karma-test-shim.js:131230:42)
at step (karma-test-shim.js:131170:23)
NOTE: the effects use a service which involves writing to PouchDB. However, the issue doesn't seem related to that
and also the effects work in the running app.
The full code is a Ionic 3 app and be found here (just clone, npm i and npm run test)
UPDATE:
With ReplaySubject it works, but not with hot/cold marbles
const item = makeItem(Faker.random.word);
actions = new ReplaySubject(1) // = Observable + Observer, 1 = buffer size
actions.next(new AddItem({ item }));
effects.addItem$.subscribe(result => {
expect(result).toEqual(new AddUpdateItemSuccess({ item }));
});
My question was answered by #phillipzada at the Github issue I posted.
For anyone checking this out later, I report here the answer:
Looks like this is a RxJS issue when using promises using marbles. https://stackoverflow.com/a/46313743/4148561
I did manage to do a bit of a hack which should work, however, you will need to put a separate test the service is being called unless you can update the service to return an observable instead of a promise.
Essentially what I did was extract the Observable.fromPromise call into its own "internal function" which we can mock to simulate a call to the service, then it looks from there.
This way you can test the internal function _addItem without using marbles.
Effect
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import { Injectable } from '#angular/core';
import { Actions, Effect } from '#ngrx/effects';
import { Action } from '#ngrx/store';
import { Observable } from 'rxjs/Observable';
export const ADD_ITEM = 'Add Item';
export const ADD_UPDATE_ITEM_SUCCESS = 'Add Item Success';
export class AddItem implements Action {
type: string = ADD_ITEM;
constructor(public payload: { item: any }) { }
}
export class AddUpdateItemSuccess implements Action {
type: string = ADD_UPDATE_ITEM_SUCCESS;
constructor(public payload: { item: any }) { }
}
export class Item {
}
export class ListingService {
add(item: Item) {
return new Promise((resolve, reject) => { resolve(item); });
}
}
#Injectable()
export class SutEffect {
_addItem(payload: { item: Item }) {
return Observable.fromPromise(this._listService.add(payload.item));
}
#Effect() addItem$ = this._actions$
.ofType<AddItem>(ADD_ITEM)
.map(action => action.payload)
.mergeMap<{ item: Item }, Observable<Item>>(payload => {
return this._addItem(payload).map(item => new AddUpdateItemSuccess({
item,
}));
});
constructor(
private _actions$: Actions,
private _listService: ListingService) {
}
}
Spec
import { cold, hot, getTestScheduler } from 'jasmine-marbles';
import { async, TestBed } from '#angular/core/testing';
import { Actions } from '#ngrx/effects';
import { Store, StoreModule } from '#ngrx/store';
import { getTestActions, TestActions } from 'app/tests/sut.helpers';
import { AddItem, AddUpdateItemSuccess, ListingService, SutEffect } from './sut.effect';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
describe('Effect Tests', () => {
let store: Store<any>;
let storeSpy: jasmine.Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({})
],
providers: [
SutEffect,
{
provide: ListingService,
useValue: jasmine.createSpyObj('ListingService', ['add'])
},
{
provide: Actions,
useFactory: getTestActions
}
]
});
store = TestBed.get(Store);
storeSpy = spyOn(store, 'dispatch').and.callThrough();
storeSpy = spyOn(store, 'select').and.callThrough();
}));
function setup() {
return {
effects: TestBed.get(SutEffect) as SutEffect,
listingService: TestBed.get(ListingService) as jasmine.SpyObj<ListingService>,
actions$: TestBed.get(Actions) as TestActions
};
}
fdescribe('addItem$', () => {
it('should return LoadItemsSuccess action for each item', async () => {
const { effects, listingService, actions$ } = setup();
const action = new AddItem({ item: 'test' });
const completion = new AddUpdateItemSuccess({ item: 'test' });
// mock this function which we can test later on, due to the promise issue
spyOn(effects, '_addItem').and.returnValue(Observable.of('test'));
actions$.stream = hot('-a|', { a: action });
const expected = cold('-b|', { b: completion });
expect(effects.addItem$).toBeObservable(expected);
expect(effects._addItem).toHaveBeenCalled();
});
})
})
Helpers
import { Actions } from '#ngrx/effects';
import { Observable } from 'rxjs/Observable';
import { empty } from 'rxjs/observable/empty';
export class TestActions extends Actions {
constructor() {
super(empty());
}
set stream(source: Observable<any>) {
this.source = source;
}
}
export function getTestActions() {
return new TestActions();
}