how do I mock out http post in Angular without using TestBed?

前端 未结 3 1876
一向
一向 2021-01-17 05:25

How do I unit test this login function, specifically the http post part? The http mock I made is not coded correctly to get into the \'if...else\' section of the code. I d

3条回答
  •  有刺的猬
    2021-01-17 05:50

    TestBed is generally preferable way to test Angular services.

    Despite what the official guide says,

    Isolated unit tests examine an instance of a class all by itself without any dependence on Angular or any injected values. The tester creates a test instance of the class with new, supplying test doubles for the constructor parameters as needed, and then probes the test instance API surface.

    You should write isolated unit tests for pipes and services.

    isolated tests don't address DI testing. When a class is instantiated with new, its DI decorators (@Injectable, @Inject) are not tested.

    Http tests are also easier to write and maintain when MockBackend is involved.

    When performance becomes a real concern, some tests can be converted from TestBed to isolated. In this case Http API should be replicated with Jasmine mocks. In order to get full coverage, all functions calls should be tested. The test will look like

      mockHttp = jasmine.createSpyObj(['post']);
      service = new AuthenticationService(mockHttp);
      ...
    
      it(..., fakeAsync(async () => {
        const bodyMock = { access_token: 'foo' };
        const responseMock = { json: jasmine.createSpy().and.returnValue(bodyMock) };
        const responseMock$ = Observable.of(responseMock);
        mockHttp.post.and.returnValue(responseMock$);
        
        const login$ = service.login(...);
        
        expect(mockHttp.post).toHaveBeenCalledTimes(1);
        
        const postArgs = callback.calls.first().args;
        expect(postArgs).toEqual([..., ..., jasmine.any(RequestOptions));
        
        const requestOptions = postArgs[2];
        expect(requestOptions.headers).toEqual(jasmine.any(Headers));
        expect(Array.from(requestOptions.headers._headers)).toEqual([
          ['Content-Type', ['application/json']],
          ['accept', ['application/json']]
        ]);
        
        expect(login$).toEqual(jasmine.any(Observable));
        const login = await login$.toPromise();
    
        expect(responseMock.json).toHaveBeenCalled();
        expect(service.token).toBe('foo');
        expect(localStorage.setItem).toHaveBeenCalledWith(...);
        expect(login).toBe(true);
      }));
    

    Then another test is performed with bodyMock that doesn't have access_token.

    It should be noticed that localStorage should be stubbed as well in order to be properly tested. For testability reasons it's beneficial to use local storage service via DI instead.

提交回复
热议问题