Search code examples
angularunit-testingtestingmockingng-mocks

How to mock an object and verify a given parameter was provided to this mock


I'm trying to test an HttpInterceptor, with the help of ng-mocks:

    @Injectable()
    export class AuthTokenInterceptor implements HttpInterceptor {
      constructor(private store: Store) {}
    
      intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const currentToken = this.store.selectSnapshot(state => state.accounts.accessToken);
    
        if (currentToken) {
          req = req.clone({
            setHeaders: { Authorization: `Bearer ${currentToken}` },
          });
        }
        return next.handle(req);
      }
    }

I'm having trouble to see how I could provide the next parameter, especially since I want to check that the object has the setHeaders property present(or not):

    describe('Unauth guard test', () => {
      beforeEach(() => {
        return MockBuilder(AuthTokenInterceptor, AppModule).mock(Store);
      });
    
      it('should allow access if not authenticated', () => {
        //Arrange
        const fakeToken = 'fakeToken';
        const storeDispatchSpy = MockInstance(Store, 'selectSnapshot', jasmine.createSpy().and.returnValue(fakeToken));
        const httpHandlerSpy = MockInstance(HttpHandler, 'handle', jasmine.createSpy().and.returnValue(fakeToken));
        const fixture = MockRender(AuthTokenInterceptor);
        const interceptor = fixture.point.componentInstance;
       const request = new HttpRequest('GET', 'http://localhost:4200/');
    
        //Act
        const result = interceptor.intercept(request, httpHandlerSpy.???);//How to provide a mock of HttpHandler?
    
        //Assert
        expect(storeDispatchSpy).toHaveBeenCalled();
        expect(httpHandlerSpy).toHaveBeenCalledOnceWith(???);//How to verify that one parameter of the HttpRequest is set to specific value?
      });
    });

But how to provide a mocked instance to the intercept method? and more complex, how to check that my spy have been called with an object that has a specific value?


Solution

  • In your case, you need to provide mockStore + isolate your interceptor from other interceptors.

    An example from ng-mocks docs and http interceptors.

    https://codesandbox.io/s/intelligent-stallman-9bwup0?file=/src/test.spec.ts

    describe('AuthTokenInterceptor', () => {
      beforeEach(() => {
        return (
          MockBuilder(AuthTokenInterceptor, AppModule)
            // required for interceptors
            .exclude(NG_MOCKS_INTERCEPTORS)
            .keep(HTTP_INTERCEPTORS)
    
            .replace(HttpClientModule, HttpClientTestingModule)
        );
      });
    
      it('adds header', () => {
        // creating an empty fixture
        const fixture = MockRender('', null, false);
    
        // stubbing store
        const store = ngMocks.findInstance(Store);
        ngMocks.stubMember(store, 'selectSnapshot', callback =>
          callback({
            accounts: {
              accessToken: 'Testing',
            },
          }),
        );
    
        // render
        fixture.detectChanges();
    
        // instances to test how HttpClient uses interceptors
        const client = ngMocks.findInstance(HttpClient);
        const httpMock = ngMocks.findInstance(HttpTestingController);
    
        // Let's do a simple request.
        client.get('/target').subscribe();
    
        // Now we can assert that a header has been added to the request.
        const req = httpMock.expectOne('/target');
        req.flush('');
        httpMock.verify();
    
        // asserting headers
        expect(req.request.headers.get('Authorization')).toEqual(
          'Bearer Testing',
        );
      });
    });