Search code examples
angulartypescriptjasmineangular-httpclient

Angular - Use Jasmine to spy on chained methods within HttpClient


I have a situation where I need to spy on a method that is called after another method is called.

Here's the class/method under test:

@Injectable()
export class SomeService {

  constructor(private customHttpClient: CustomHttpClient) {
  }

    updateSomethingCool(signerVO: SignerVO): Observable<SignerVO> {

    // ...

    return this.customHttpClient.withCustomOverrides(new CustomErrorHandlerHttpInterceptorOverride({ passthroughStatusCodes: [BAD_REQUEST, BAD_GATEWAY] }))
        .put<SignerVO>(`/my/url/goes/here`, signerVO);
    }
}

This class uses CustomHttpClient which looks like this:

    @Injectable()
    export class CustomHttpClient extends HttpClient {
        private interceptors: HttpInterceptor[] | null = null;

        constructor(private injector: Injector,
            originalHandler: HttpHandler, private originalBackend: HttpBackend) {
            super(originalHandler);
        }

        public withCustomOverrides(...overrides: CustomHttpInterceptorOverride[]): HttpClient {

            // do very customizable things here

            return new CustomDelegatingHttpClient(
                new CustomHttpInterceptingHandler(this.originalBackend, this.interceptors, overrides));
        }
    }

    export class CustomDelegatingHttpClient extends HttpClient {
        constructor(private delegate: HttpHandler) {
            super(delegate);
        }
}

Here's my attempt at unit testing that the put method has indeed been called, hence I need to spy on the put method:

describe(SomeService.name, () => {
let service: SomeService;
let customHttpClient: CustomHttpClient;

let emptySignerVO: SignerVO = new SignerVO();

beforeEach(() => {
    customHttpClient= <CustomHttpClient>{};
    customHttpClient.put = () => null;
    customHttpClient.withCustomOverrides = () => null;

    service = new SomeService(customHttpClient);
});

describe('updateSomethingCool', () => {

    it('calls put', () => {
        spyOn(customHttpClient, 'put').and.stub();

        service.updateSomethingCool(emptySignerVO);

        expect(customHttpClient.put).toHaveBeenCalled();
    });
});

Now clearly when I run this I receive this failure message:

TypeError: Cannot read property 'put' of null

However, I do not know exactly how to define either the put or withCustomOverrides methods in the beforeEach portion of the test.

Note that CustomHttpClient is simply a customized wrapper class around Angular's HttpClient that allows some more detailed functionality.

Thank you for your help!


Solution

  • Well, in the end I wasn't that far off actually. The actual test code was fine; the beforeEach() method needs updated as follows:

    beforeEach(() => {
        customHttpClient = <CustomHttpClient>{};
        customHttpClient.put = () => null;
        customHttpClient.withCustomOverrides = () => customHttpClient;
    
        service = new SomeService(customHttpClient);
    });
    

    I basically just had to assign the customHttpClient object to the .withCustomOverides method.

    Which, if you look at the flow of the method chaining in the real call, actually makes sense.