How to test that an NGXS action has been dispatched, amongst some other tests?

651 views Asked by At

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?

2

There are 2 answers

0
J4N On

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
  });
});
0
satanTime On

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();
  });
});