Let's say I have a service that makes use of HttpClient,
@Injectable()
export class MyService {
constructor(protected httpClient: HttpClient) { .. }
}
And then a component that makes use of this service.
@Component({
selector: 'my-component'
})
export class SendSmsComponent {
constructor(private MyService) { .. }
}
How to test this component while mocking the HttpClient and not the whole service?
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [
{ provide: MyService, useClass: MyService } // ?
]
}).compileComponents();
httpMock = TestBed.get(HttpTestingController); // ?
You can put the response json in the asset folder and do the testing. You can also configure the url to be selected based on the environment variable so that in the prod build actual url will be taken and in dev the dummy one.
HttpClient's extensibility lies in the HttpMessageHandler passed to the constructor. Its intent is to allow platform specific implementations, but you can also mock it. There's no need to create a decorator wrapper for HttpClient.
Prefer spies as they are usually the best way to mock services. These standard testing techniques are great for unit testing services in isolation. However, you almost always inject services into application classes using Angular dependency injection and you should have tests that reflect that usage pattern.
The HttpClientTestingModule allows you to easily mock HTTP requests by providing you with the HttpTestingController service.
To mock HttpClient you can use HttpClientTestingModule with HttpTestingController
Sample code to accomplish the same
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);
});
});
});
This is the approach I follow while testing HttpClient
Create mock HttpClient
object
const httpClientSpy = jasmine.createSpyObj('HttpClient', ['post', 'get']);
Injecting mock object in providers
providers: [{ provide: HttpClient, useValue: httpClientSpy }]
Return dummy valued within beforeEach()
or it()
httpClientSpy.post.and.returnValue(of({ status: 200, data: {} }));
httpClientSpy.get.and.returnValue(of({ status: 200, data: {} }));
Example test case
it('should return data for abc endpoint', () => {
service.methodWithHttpRequest().subscribe(data => expect(data.status).toBe(200));
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With