如何在Angular的组件测试中模拟提供的服务中的HttpClient?

Mar*_*tin 17 unit-testing angular

假设我有一个使用HttpClient的服务,

@Injectable()
export class MyService {
  constructor(protected httpClient: HttpClient) { .. }
}
Run Code Online (Sandbox Code Playgroud)

然后是一个使用此服务的组件.

@Component({
  selector: 'my-component'
})

export class SendSmsComponent {
  constructor(private MyService) { .. }
}
Run Code Online (Sandbox Code Playgroud)

如何在模拟HttpClient而不是整个服务的同时测试这个组件?

TestBed.configureTestingModule({
  declarations: [MyComponent],
  providers: [
    { provide: MyService, useClass: MyService } // ?
  ]
}).compileComponents();

httpMock = TestBed.get(HttpTestingController); // ?
Run Code Online (Sandbox Code Playgroud)

小智 23

要模拟 HttpClient,您可以将HttpClientTestingModuleHttpTestingController一起使用

完成相同操作的示例代码

import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Type } from '@angular/core';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { SendSmsComponent } from './send-sms/send-sms.component';
import { ApiService } from '@services/api.service';

describe('SendSmsComponent ', () => {
  let fixture: ComponentFixture<SendSmsComponent>;
  let app: SendSmsComponent;
  let httpMock: HttpTestingController;

  describe('SendSmsComponent ', () => {
    beforeEach(async () => {
      TestBed.configureTestingModule({
        imports: [
          HttpClientTestingModule,
        ],
        declarations: [
          SendSmsComponent,
        ],
        providers: [
          ApiService,
        ],
      });

      await TestBed.compileComponents();

      fixture = TestBed.createComponent(SendSmsComponent);
      app = fixture.componentInstance;
      httpMock = fixture.debugElement.injector.get<HttpTestingController>(HttpTestingController as Type<HttpTestingController>);

      fixture.detectChanges();
    });

    afterEach(() => {
      httpMock.verify();
    });

    it('test your http call', () => {
      const dummyUsers = [
        { name: 'John' },
      ];

      app.getUsers();
      const req = httpMock.expectOne(`${url}/users`);
      req.flush(dummyUsers);

      expect(req.request.method).toBe('GET');
      expect(app.users).toEqual(dummyUsers);
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 所有这些代码真的有必要模拟 HttpClient 吗?看起来像是从项目代码库中复制粘贴的。魔法到底发生在哪里?一些解释会很有用。 (2认同)

Aak*_*ani 17

这是我在测试时遵循的方法HttpClient

  1. 创建模拟HttpClient对象

    const httpClientSpy = jasmine.createSpyObj('HttpClient', ['post', 'get']);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 注入模拟对象providers

    providers: [{ provide: HttpClient, useValue: httpClientSpy }]
    
    Run Code Online (Sandbox Code Playgroud)
  3. 返回值在beforeEach()或 之内的虚拟值it()

    httpClientSpy.post.and.returnValue(of({ status: 200, data: {} }));
    httpClientSpy.get.and.returnValue(of({ status: 200, data: {} }));
    
    Run Code Online (Sandbox Code Playgroud)
  4. 测试用例示例

    it('should return data for abc endpoint', () => {
      service.methodWithHttpRequest().subscribe(data => expect(data.status).toBe(200));
    });
    
    Run Code Online (Sandbox Code Playgroud)