-
Notifications
You must be signed in to change notification settings - Fork 404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🚀[FEATURE]: How to mock @Select in ngxs when using a mock store #482
Comments
Sorry but you HAVE to inject ngxs module for this to work :( |
@amcdnl We could look at making a testability Helper for this. It's one of the items on my list. |
@amcdnl @markwhitfeld I can see it cannot be done now, but I would think it would be a good addition to make the framework a bit more open for mocking, so that we dont have to mock all our services that are used withing the states. Can this maybe be re-opened and converted to a feature request? |
Love the simplicity & clarity of ngxs vs redux. However, totally agree should not have to inject real dependencies to test components. Haven't dug into source code but was able to 'hack' around it by redefining the property in the test. Where 'component' is the component under test and 'selectPropertyName' is the name of the property decorated with '@select'. Once redefined, can simply provide observable you control in test flow: Not the ideal solution but feels like spirit of what i'm testing remains unsullied. |
@BradleyHill thanks! you saved my day |
@BradleyHill, @wouterv where exactly in the testing flow do you redefine your component properties? beforeEach(() => {
fixture = TestBed.createComponent(RequestStartAllowanceComponent);
component = fixture.componentInstance;
Object.defineProperty(component, 'prop$', { writable: true });
component.prop$ = of('value');
fixture.detectChanges();
}); |
@regexer-github Your code is exactly how we test it and it works for us. Maybe you can share your error? |
As @wouterv says, we have same thing save the override is in the test and not the The only other thing to note is if you are binding to the Example:
(Authoring code inline so possible errors) Ideally |
Well, I somehow cannot get this to fail anymore... I assume ng test did recompile correctly :/ |
I'm in a similar situation but the
|
Sorry, I just found a solution:
|
How would you do this when your component template references another component that contains a public Would you just mock the component? |
It feels like I'm trading ngrx boiler plate for Angular services that sit between my components and ngxs in order to make my code testable. |
Hi everyone. We would like to add some test helpers for ngxs soon to make testing easier. Would any of you like to assist us in this effort? |
I found this way makes it easy to write unit tests for components without calling actions. Firstly I write a plugin to ignore actions and store them in an array to later assert if that action was called:
Then I use it in my testing module:
Now I can assert if actions were called in my tests without causing any side effects:
There is no need to write mock for |
We have espoused the philosophy that components are dumb as possible. They render values from state and communicate user actions to state via actions. That's it. Logic in components is bad. We write tests against rendering by seeding different states to our bound To test the user interaction, we simply spy on store dispatch in setup: Ridiculously easy. Has gotten us a long ways. |
@markwhitfeld Love the product. Ngrx sans all the ceremony. With redux pattern implementations, found great developers authored meh change and meh developers authored horrible code. This package strips down to the essence. We haven't had many issues save the hack mentioned above but if have ideas and i can see value in them, may be able to throw some resources at them to solve them on our project and then push the code back to the github repo. Ngxs has been wonderful addition to project so would love to give back if can find use cases. I'm hug test advocate also so anything to help promote or simplify testing, no sell necessary. |
@gforceg As for your first question, typically mock out second component. It's a whole "unit" thing. You should only care about the primary component, not any transitive effects down the line. Law Of Demeter is testing. Second question is little more nebulous. Not sure you are making a concession for testability. However, ngxs (or ngrx or any cqrs solution) is no panacea. You can accomplish your goal with ngxs, rolled services, or raw http client calls for that matter). Just a decision need to make depending on many factors. |
Hi Bradley, I did exactly what you suggested. However it keeps telling me that my “prop$” is undefined. Any hints? Thanks. |
@theyCallMeJay we are now working on a new package |
@splincode That is amazing. May I ask the estimate of your release date for that package? I'm really scratching my head at this moment since my company is asking for unit testing coverage for the application but I can't provide none atm. |
@wouterv is it possible to share your piece of unit testing code that worked for you? Thanks |
It was stated above, but for clarity, here goes:
|
@wouterv, thanks! that still did not work for me though i did the same thing. Do you mind sharing what is inside your testbed configure? Thanks. |
@theyCallMeJay I put in all we have. |
@wouterv , my bad. I somehow solved it by removing store from provide. Here is my snippet in case others have same issues as well. beforeEach(async(() => { |
@theyCallMeJay NgxsModule is debatable, but you really shouldn't (have a reason to) have NgxsReduxDevtoolsPluginModule or NgxsLoggerPluginModule as a dependency. Those are not meant to be used in production and should not be in your dependencies except when running in dev (not test) environment. |
This problem is now considered here: ngxs-labs/testing#3 |
Hello there! At work, we also need to mock the selects while testing our components. I've seen in a recent commit that @select decorator will be considered deprecated in future releases so I am not going to focus on this here. (Because it won't work with this approach) I've used NGXS in the past and was able to convince my team to use it over NGRX. The main argument was that the programming style of NGXS is more in line with Angular. I've seen the internal NgxsTestBed and I'm not a fan mainly for the same reason. It's not the Angular way. Inspired by the HttpTestingModule, I have quickly created an NgxsTestingModule. I would like some feedback. import { Inject, Injectable, InjectionToken, NgModule } from '@angular/core';
import {NgxsModule, Store} from '@ngxs/store'
import { concat, Observable, ReplaySubject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
const ORIGINAL_STORE = new InjectionToken<Store>('OriginalStore')
@Injectable()
class MockedSelectors {
private mockedSelectors: { key: any; value: ReplaySubject<any> }[] = []
getMockedSelector(key: any) {
let match = this.mockedSelectors.find( s => s.key === key)
if(!match) {
match = {key, value: new ReplaySubject<any>(1)}
this.mockedSelectors.push(match)
}
return match.value
}
}
@Injectable()
class StoreInterceptor {
constructor(
private mockedSelectors: MockedSelectors,
@Inject(ORIGINAL_STORE) private store: Store
){}
// interceptors
dispatch(actionOrActions: any | any[]): Observable<any> {
return this.store.dispatch(actionOrActions)
}
select(selector: any): Observable<any> {
const mockedSelector = this.mockedSelectors.getMockedSelector(selector)
return concat(
this.store.select(selector).pipe(takeUntil(mockedSelector)),
mockedSelector
)
}
selectOnce(selector: any): Observable<any> {
return this.store.selectOnce(selector)
}
selectSnapshot(selector: any): any {
return this.store.selectSnapshot(selector)
}
subscribe(fn?: (value: any) => void): Subscription {
return this.store.subscribe(fn)
}
snapshot(): any {
return this.store.snapshot()
}
reset(state: any) {
this.store.reset(state)
}
}
@Injectable()
export class NgxsTestingController {
constructor(private mockedSelector: MockedSelectors) {}
mockSelector(selector: any) {
return this.mockedSelector.getMockedSelector(selector)
}
}
@NgModule()
export class NgxsTestingModule {
static forRoot(...args) {
const ngxsModule = NgxsModule.forRoot(...args)
return {
...ngxsModule,
providers: [
{provide: Store, useClass: StoreInterceptor},
{provide: ORIGINAL_STORE, useClass: Store},
MockedSelectors,
NgxsTestingController,
...ngxsModule.providers.filter(p => p !== Store)
]
}
}
} Usage: import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { Store } from '@ngxs/store';
import { SessionState } from '@sdum/core/store';
import {NgxsTestingModule, NgxsTestingController} from './ngxs-testing.module'
@Component({
selector: 'test-component',
template: ''
})
export class TestComponent {
authorities$ = this.store.select(SessionState.authorities)
constructor(private store: Store) { }
}
describe('NgxsTestingModule', () => {
let fixture: ComponentFixture<TestComponent>
let component: TestComponent
let ngxsTestingController: NgxsTestingController
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
NgxsTestingModule.forRoot(),
],
declarations: [
TestComponent
]
}).compileComponents()
ngxsTestingController = TestBed.inject(NgxsTestingController)
fixture = TestBed.createComponent(TestComponent)
component = fixture.componentInstance
})
describe('SessionState.authorities', () => {
it('should be mocked', async () => {
ngxsTestingController.mockSelector(SessionState.authorities).next(['READ', 'WRITE', 'DELETE'])
expect(await component.authorities$.toPromise()).toEqual(['READ', 'WRITE', 'DELETE'])
});
});
}) |
Hello, where did you see that @select in future versions will be deprecated? |
Is there any news or plan to upgrade the tools in the library to mock ngxs properly, If you take NgRx for example they have done somehting good , https://ngrx.io/guide/store/testing , It's really a major downgrade for ngxs, |
@koraxos Unsure if you already tried the approach from @FortinFred but it works with the exception of one case wonderfully: Maybe, you have another idea to solve this issue. If that's the case, the approach could be developed into a ngxs testing package that provides that functionality. This could be an discussion point for the design of the metadata API of version 4.0 as well, but as this issue ranks as low in priority, I am unsure how much this is going to influence the decisions by the NGXS core team. |
Found a solution for anyone interested: export function TestSelectorName(name?: string) {
return function innerFunction(target, propertyKey: string, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const selector = originalMethod.apply(this, args);
(selector as any).selectorName = name || propertyKey;
(selector as any).selectorArgs = args;
return selector;
};
};
} We just annotate our static methods with For the tests you can then use the same selector with your specified arguments and FortinFreds approach: ngxsTestingController.mockSelector(CustomState.customSelector('example-uuid')).next({property: a}) This required small adjustments to the implementation of FortinFreds mock store, but this is manageable. |
I am using ngxs for state handling in angular, and I am trying to test our components as units, so preferably only with mock stores, states etc.
What we have in our component is something like:
And our fixture setup looks like this, so we can check calls on the store:
But when we run this, we get the following error:
I could enable the entire ngxs module for this, but then I would need to create services mocks to inject into state objects, which I do not like because I am then not testing the component in isolation anymore. I tried to create a mock SelectFactory, but it seems it is not exported from the module.
Is there a way to mock the SelectFactory, or inject some mocks into the platformList$ directly? Other suggestions?
PS: I also asked this on stackoverflow, but no real answer was given: https://stackoverflow.com/questions/51082002/how-to-mock-select-in-ngxs-when-using-a-mock-store
The text was updated successfully, but these errors were encountered: