Angular 2
Mocking @ ngrx / Store
Zoeken…
Invoering
parameters
naam | Beschrijving |
---|---|
waarde | volgende waarde die moet worden waargenomen |
fout | Beschrijving |
dwalen | fout die moet worden gegooid |
super | Beschrijving |
actie $ | mock Observer die niets doet tenzij gedefinieerd om dit in de mockklasse te doen |
actionReducer $ | mock Observer die niets doet tenzij gedefinieerd om dit in de mockklasse te doen |
obs $ | mock Observable |
Opmerkingen
Waarnemer een generiek, maar moet van het type zijn any
aan vermijden unit testing complexiteit. De reden voor deze complexiteit is dat de constructor van Store Observer-argumenten verwacht met verschillende generieke typen. Het gebruik van any
voorkomt deze complicatie.
Het is mogelijk om null-waarden door te geven aan de superconstructor van StoreMock, maar dit beperkt het aantal beweringen dat kan worden gebruikt om de klasse verderop te testen.
De component die in dit voorbeeld wordt gebruikt, wordt alleen gebruikt als context voor hoe men Store zou kunnen injecteren als een voorziening in de testopstelling.
Waarnemer Mock
class ObserverMock implements Observer<any> {
closed?: boolean = false; // inherited from Observer
nextVal: any = ''; // variable I made up
constructor() {}
next = (value: any): void => { this.nextVal = value; };
error = (err: any): void => { console.error(err); };
complete = (): void => { this.closed = true; }
}
let actionReducer$: ObserverMock = new ObserverMock();
let action$: ObserverMock = new ObserverMock();
let obs$: Observable<any> = new Observable<any>();
class StoreMock extends Store<any> {
constructor() {
super(action$, actionReducer$, obs$);
}
}
describe('Component:Typeahead', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [...],
declarations: [Typeahead],
providers: [
{provide: Store, useClass: StoreMock} // NOTICE useClass instead of useValue
]
}).compileComponents();
});
});
Eenheidstest voor component met onechte winkel
Dit is een eenheidstest van een component met Store als afhankelijkheid. Hier maken we een nieuwe klasse genaamd MockStore die in onze component wordt geïnjecteerd in plaats van de gebruikelijke Store.
import { Injectable } from '@angular/core';
import { TestBed, async} from '@angular/core/testing';
import { AppComponent } from './app.component';
import {DumbComponentComponent} from "./dumb-component/dumb-component.component";
import {SmartComponentComponent} from "./smart-component/smart-component.component";
import {mainReducer} from "./state-management/reducers/main-reducer";
import { StoreModule } from "@ngrx/store";
import { Store } from "@ngrx/store";
import {Observable} from "rxjs";
class MockStore {
public dispatch(obj) {
console.log('dispatching from the mock store!')
}
public select(obj) {
console.log('selecting from the mock store!');
return Observable.of({})
}
}
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
SmartComponentComponent,
DumbComponentComponent,
],
imports: [
StoreModule.provideStore({mainReducer})
],
providers: [
{provide: Store, useClass: MockStore}
]
});
});
it('should create the app', async(() => {
let fixture = TestBed.createComponent(AppComponent);
let app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
Eenheidstest voor het bespioneren van componenten in de winkel
Dit is een eenheidstest van een component met Store als afhankelijkheid. Hier kunnen we een winkel met de standaard "initiële status" gebruiken en voorkomen dat deze daadwerkelijk acties verzendt wanneer store.dispatch () wordt aangeroepen.
import {TestBed, async} from '@angular/core/testing';
import {AppComponent} from './app.component';
import {DumbComponentComponent} from "./dumb-component/dumb-component.component";
import {SmartComponentComponent} from "./smart-component/smart-component.component";
import {mainReducer} from "./state-management/reducers/main-reducer";
import {StoreModule} from "@ngrx/store";
import {Store} from "@ngrx/store";
import {Observable} from "rxjs";
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
SmartComponentComponent,
DumbComponentComponent,
],
imports: [
StoreModule.provideStore({mainReducer})
]
});
});
it('should create the app', async(() => {
let fixture = TestBed.createComponent(AppComponent);
let app = fixture.debugElement.componentInstance;
var mockStore = fixture.debugElement.injector.get(Store);
var storeSpy = spyOn(mockStore, 'dispatch').and.callFake(function () {
console.log('dispatching from the spy!');
});
}));
});
Hoek 2 - Mock Observable (service + component)
onderhoud
- Ik heb postservice gemaakt met de postRequest-methode.
import {Injectable} from '@angular/core';
import {Http, Headers, Response} from "@angular/http";
import {PostModel} from "./PostModel";
import 'rxjs/add/operator/map';
import {Observable} from "rxjs";
@Injectable()
export class PostService {
constructor(private _http: Http) {
}
postRequest(postModel: PostModel) : Observable<Response> {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this._http.post("/postUrl", postModel, {headers})
.map(res => res.json());
}
}
bestanddeel
- Ik heb een component met resultaatparameter en functie postExample gemaakt die aanroep tot postService aanroept.
- wanneer de postaanvraag geslaagd is dan de resultaatparameter 'Succes' moet zijn 'anders' Mislukt '
import {Component} from '@angular/core';
import {PostService} from "./PostService";
import {PostModel} from "./PostModel";
@Component({
selector: 'app-post',
templateUrl: './post.component.html',
styleUrls: ['./post.component.scss'],
providers : [PostService]
})
export class PostComponent{
constructor(private _postService : PostService) {
let postModel = new PostModel();
result : string = null;
postExample(){
this._postService.postRequest(this.postModel)
.subscribe(
() => {
this.result = 'Success';
},
err => this.result = 'Fail'
)
}
}
testdienst
- wanneer u de service wilt testen, moet u mockBackend gebruiken als u http gebruikt. en injecteer het erop.
- u moet ook postService injecteren.
describe('Test PostService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
PostService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
]
});
});
it('sendPostRequest function return Observable', inject([PostService, MockBackend], (service: PostService, mockBackend: MockBackend) => {
let mockPostModel = PostModel();
mockBackend.connections.subscribe((connection: MockConnection) => {
expect(connection.request.method).toEqual(RequestMethod.Post);
expect(connection.request.url.indexOf('postUrl')).not.toEqual(-1);
expect(connection.request.headers.get('Content-Type')).toEqual('application/json');
});
service
.postRequest(PostModel)
.subscribe((response) => {
expect(response).toBeDefined();
});
}));
});
testonderdeel
describe('testing post component', () => {
let component: PostComponent;
let fixture: ComponentFixture<postComponent>;
let mockRouter = {
navigate: jasmine.createSpy('navigate')
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PostComponent],
imports: [RouterTestingModule.withRoutes([]),ModalModule.forRoot() ],
providers: [PostService ,MockBackend,BaseRequestOptions,
{provide: Http, deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
},
{provide: Router, useValue: mockRouter}
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test postRequest success', inject([PostService, MockBackend], (service: PostService, mockBackend: MockBackend) => {
fixturePostComponent = TestBed.createComponent(PostComponent);
componentPostComponent = fixturePostComponent.componentInstance;
fixturePostComponent.detectChanges();
component.postExample();
let postModel = new PostModel();
let response = {
'message' : 'message',
'ok' : true
};
mockBackend.connections.subscribe((connection: MockConnection) => {
postComponent.result = 'Success'
connection.mockRespond(new Response(
new ResponseOptions({
body: response
})
))
});
service.postRequest(postModel)
.subscribe((data) => {
expect(component.result).toBeDefined();
expect(PostComponent.result).toEqual('Success');
expect(data).toEqual(response);
});
}));
});
Eenvoudige winkel
simple.action.ts
import { Action } from '@ngrx/store';
export enum simpleActionTpye {
add = "simpleAction_Add",
add_Success = "simpleAction_Add_Success"
}
export class simpleAction {
type: simpleActionTpye
constructor(public payload: number) { }
}
simple.efficts.ts
import { Effect, Actions } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { Observable } from 'rxjs';
import { simpleAction, simpleActionTpye } from './simple.action';
@Injectable()
export class simpleEffects {
@Effect()
addAction$: Observable<simpleAction> = this.actions$
.ofType(simpleActionTpye.add)
.switchMap((action: simpleAction) => {
console.log(action);
return Observable.of({ type: simpleActionTpye.add_Success, payload: action.payload })
// if you have an api use this code
// return this.http.post(url).catch().map(res=>{ type: simpleAction.add_Success, payload:res})
});
constructor(private actions$: Actions) { }
}
simple.reducer.ts
import { Action, ActionReducer } from '@ngrx/store';
import { simpleAction, simpleActionTpye } from './simple.action';
export const simpleReducer: ActionReducer<number> = (state: number = 0, action: simpleAction): number => {
switch (action.type) {
case simpleActionTpye.add_Success:
console.log(action);
return state + action.payload;
default:
return state;
}
}
opslag / index.ts
import { combineReducers, ActionReducer, Action, StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { ModuleWithProviders } from '@angular/core';
import { compose } from '@ngrx/core';
import { simpleReducer } from "./simple/simple.reducer";
import { simpleEffects } from "./simple/simple.efficts";
export interface IAppState {
sum: number;
}
// all new reducers should be define here
const reducers = {
sum: simpleReducer
};
export const store: ModuleWithProviders = StoreModule.forRoot(reducers);
export const effects: ModuleWithProviders[] = [
EffectsModule.forRoot([simpleEffects])
];
app.module.ts
import { BrowserModule } from '@angular/platform-browser'
import { NgModule } from '@angular/core';
import { effects, store } from "./Store/index";
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
// store
store,
effects
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { IAppState } from './Store/index';
import { simpleActionTpye } from './Store/simple/simple.action';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(private store: Store<IAppState>) {
store.select(s => s.sum).subscribe((res) => {
console.log(res);
})
this.store.dispatch({
type: simpleActionTpye.add,
payload: 1
})
this.store.dispatch({
type: simpleActionTpye.add,
payload: 2
})
this.store.dispatch({
type: simpleActionTpye.add,
payload: 3
})
}
}
resultaat 0 1 3 6