I'm using NGXS with a facade pattern(basically, components have access to an angular service, and the angular service reference the store, dispatch the method if needed.
I'm in the process of testing one method of my service.
This method:
- Make an HTTP Request
- Dispatch an Action
How can I easily check that my action has been dispatched and do some other checks on the HTTP Request?
(I'm using ng-mocks to mock a lot of my components).
Here is my try:
describe('AccountService', () => {
beforeEach(() => {
return MockBuilder(AccountService, AccountModule).replace(HttpClientModule, HttpClientTestingModule).mock(Store);
});
it('should be created', () => {
//Act
const service = MockRender(AccountService).point.componentInstance;
//Assert
expect(service).toBeTruthy();
});
it('should retrieve the access token on initialization', async () => {
//Arrange
const fixture = MockRender(AccountService);
const service = fixture.point.componentInstance;
const testingController: HttpTestingController = fixture.debugElement.injector.get(HttpTestingController);
const storeDispatchSpy = MockInstance(Store, 'dispatch', jasmine.createSpy());
//Act
const asyncCall = service.initialize();
//Assert
const request = testingController.expectOne(environment.backendUrl 'accounts/refresh-token');
expect(request.request.method).toEqual('GET');
request.flush({});
testingController.verify();
await asyncCall; //Ensure the method has returned
expect(storeDispatchSpy).toHaveBeenCalledWith(jasmine.any(Logout));
});
});
But it even if I've an console.log
appearing just where I'm dispatching a Logout
action, my last expect
fails. Actually, it even fails if I've a expect(storeDispatchSpy).toHaveBeenCalled();
instead of the toHaveBeenCalledWith
.
Just to be clear: The scope of this test is only to check an action has been dispatched on the store. The effects of this actions on the store will be tested in unit tests of the store itself(where I will probably have to instantiate a real store to apply the changes).
Any idea what I'm missing?
CodePudding user response:
I'm so dum, but I hope it can help some other people.
The code of my question almost work, in fact the only issue is that the spy was created after the creation of the Service.
This code seems to work:
describe('AccountService', () => {
beforeEach(() => {
return MockBuilder(AccountService, AccountModule).replace(HttpClientModule, HttpClientTestingModule).mock(Store);
});
it('should be created', () => {
//Act
const service = MockRender(AccountService).point.componentInstance;
//Assert
expect(service).toBeTruthy();
});
it('should retrieve the access token on initialization', async () => {
//Arrange
const storeDispatchSpy = MockInstance(Store, 'dispatch', jasmine.createSpy().and.returnValue(EMPTY));
const fixture = MockRender(AccountService);
const service = fixture.point.componentInstance;
const testingController: HttpTestingController = fixture.debugElement.injector.get(HttpTestingController);
//Act
const asyncCall = service.initialize();
//Assert
const request = testingController.expectOne(environment.backendUrl 'accounts/refresh-token');
expect(request.request.method).toEqual('GET');
request.flush({});
testingController.verify();
await asyncCall; //Ensure the method has returned
expect(storeDispatchSpy).toHaveBeenCalledWith(jasmine.any(Logout)); //Should be a logout because no valid token has been provided
});
});
CodePudding user response:
to have a spy before the render, you need to use MockRenderFactory
fdescribe('ngxs:MockBuilder', () => {
beforeEach(() =>
MockBuilder(AccountService, AccountModule)
.keep(NgxsModule.forRoot().ngModule)
// .keep(NgxsModule.forFeature().ngModule), // if needed
);
const factory = MockRenderFactory(AccountService);
beforeEach(() => factory.configureTestBed());
it('selects the value', () => {
const store = TestBed.inject(Store);
const dispatchSpy = spyOn(store, 'dispatch');
const fixture = factory();
// asserting
expect(dispatchSpy).toHaveBeenCalled();
});
});