Mocking ngrx / store

This refers to the official release of Angular 2. I know that unit testing has changed a lot between beta, RC, and official release.

What's a good way to fake @ ngrx / store in unit test when it is used as a parameter in a constructor? It is not as simple as a mockery of a service.

For example, if I wanted to make fun of a service, I could do something like this:

let serviceStub = { }; // not a true mocked service, just a stub, right? let de: DebugElement; let el: HTMLElement; let nativeEl: Element; let comp: Typeahead; let fixture: ComponentFixture<Typeahead>; describe('Component:Typeahead', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [...], declarations: [Typeahead], providers: [ {provide: TypeaheadService, useValue: serviceStub} // provides the service that is being "mocked" ] }).compileComponents(); fixture = TestBed.createComponent(Typeahead); nativeEl = fixture.nativeElement; comp = fixture.componentInstance; de = fixture.debugElement; }); }); 

And it works.

For ngrx/store , however, this is not the case (if you replace Store in for TypeaheadService). I think you need to write a mock class that extends the Store and then provide this to the component under test, but I'm not sure why this is (if at all).

I just got confused how to mock ngrx/store in my unit tests and couldn't find the documentation on my website or github. Perhaps I did not pay attention to this.

+4
source share
3 answers

Thanks for posting a question and suggesting a potential solution!

As I mocked this, use the actual steps to set the initial state, that is, the finished state before each test. Here is an example

 beforeEach(inject([Store], (store: Store<ApplicationState>) => { const someFakeState = { counter: 9, counterFilter: 'A_FAKE_COUNTER_FILTER' }; store.dispatch(new myActionToSetSomeData(someFakeState)); })); 

Inside the it() block, you should now check that the component displays a counter of 9 and filtering 'A_FAKE_COUNTER_FILTER' .

You can, of course, set the state inside your block, not beforeEach , until it is created before the component is created.

+5
source

You can use forRoot (> = v4) or provideStore (<= v3) to provide data to the StoreModule, and the rest is done for you:

1 - Import it:

 import { StoreModule } from '@ngrx/store'; 

2 - Create a data layout:

 /* * Mock data */ const PAINTS = []; 

3 - Import it into the test:

 beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ StoreModule.forRoot(PAINTS) ] }) })) 

In previous versions (before v4), forRoot(PAINTS) should be used provideStore(PAINTS) . See Change Log here

+4
source

Yes, you need to make fun of ngrx/store , but not just Store. The store expects three arguments; one of the types of Observable and two types of Observer, which is the interface. So, I tried two things. Passing null values ​​to the StoreMock super() constructor, but this failed in my statement. My other solution was to implement an Observer interface with a layout class (in this case observable). That way, I could pass certain values ​​to the super StoreMock constructor.

This is just an illustrative example. ObservableMock does not actually make fun of any functionality that I am trying to test in my application. It serves as a means of protection, so the Store can be entered as a provider in the component that I am trying to verify.

Since Observer is an interface, you must implement its function declarations in mock: next , error and complete .

 class ObservableMock 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$: ObservableMock = new ObservableMock(); let action$: ObservableMock = new ObservableMock(); let obs$: Observable<any> = new Observable<any>(); class StoreMock extends Store<any> { constructor() { super(action$, actionReducer$, obs$); } } 

You can now add the Store as a provider in the Component test module.

 describe('Component:Typeahead', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [...], declarations: [Typeahead], providers: [ {provide: Store, useClass: StoreMock} // NOTICE useClass instead of useValue ] }).compileComponents(); }); }); 

I am sure there are other ways to do this. Therefore, if anyone has other answers, please write them!

+1
source

Source: https://habr.com/ru/post/1266656/


All Articles