Тестирование запросов
Как и для любой внешней зависимости, вам необходимо имитировать HTTP-бэкенд, чтобы тесты могли моделировать взаимодействие с удалённым сервером. Библиотека @angular/common/http/testing предоставляет инструменты для перехвата запросов приложения, проверки утверждений о них и имитации ответов, воспроизводящих поведение вашего бэкенда.
Библиотека для тестирования рассчитана на следующий шаблон: сначала приложение выполняет код и отправляет запросы. Затем тест проверяет, были ли отправлены определённые запросы, выполняет утверждения относительно этих запросов и, наконец, предоставляет ответы, вызывая метод «flush» для каждого ожидаемого запроса.
Наконец, тесты могут проверить, что приложение не отправляло неожиданных запросов.
Настройка тестирования
Чтобы приступить к тестированию использования HttpClient, настройте TestBed и добавьте provideHttpClientTesting() в настройки теста. HttpClient предоставляется тестовым окружением Angular, а provideHttpClientTesting() настраивает его для использования тестового бэкенда вместо реальной сети. Кроме того, он предоставляет HttpTestingController, с помощью которого можно взаимодействовать с тестовым бэкендом, задавать ожидания относительно отправленных запросов и передавать ответы на них. После настройки HttpTestingController можно внедрить из TestBed.
TestBed.configureTestingModule({
providers: [
// ... other test providers
provideHttpClientTesting(),
],
});
const httpTesting = TestBed.inject(HttpTestingController);
Теперь запросы, отправленные тестами, будут попадать в тестовый бэкенд, а не в обычный. С помощью httpTesting можно проверять утверждения об этих запросах.
Настройка HttpClient в тестах
Если для теста необходимо настроить функции HttpClient, например перехватчики, добавьте provideHttpClient(...) перед provideHttpClientTesting(). ВАЖНО: не забудьте добавить provideHttpClient() до provideHttpClientTesting(), поскольку provideHttpClientTesting() перезаписывает часть настроек provideHttpClient(). Обратный порядок может привести к сбоям тестов.
TestBed.configureTestingModule({
providers: [provideHttpClient(withInterceptors([authInterceptor])), provideHttpClientTesting()],
}); Ожидание запросов и предоставление ответов
Например, можно написать тест, который ожидает отправки GET-запроса и предоставляет имитированный ответ:
TestBed.configureTestingModule({
providers: [ConfigService, provideHttpClientTesting()],
});
const httpTesting = TestBed.inject(HttpTestingController);
// Load `ConfigService` and request the current configuration.
const service = TestBed.inject(ConfigService);
const config$ = service.getConfig<Config>();
// `firstValueFrom` subscribes to the `Observable`, which makes the HTTP request,
// and creates a `Promise` of the response.
const configPromise = firstValueFrom(config$);
// At this point, the request is pending, and we can assert it was made
// via the `HttpTestingController`:
const req = httpTesting.expectOne('/api/config', 'Request to load the configuration');
// We can assert various properties of the request if desired.
expect(req.request.method).toBe('GET');
// Flushing the request causes it to complete, delivering the result.
req.flush(DEFAULT_CONFIG);
// We can then assert that the response was successfully delivered by the `ConfigService`:
expect(await configPromise).toEqual(DEFAULT_CONFIG);
// Finally, we can assert that no other requests were made.
httpTesting.verify();
ПРИМЕЧАНИЕ: expectOne завершится ошибкой, если тест отправил более одного запроса, соответствующего заданным критериям.
Вместо проверки req.method можно использовать расширенную форму expectOne, чтобы также сопоставить метод запроса:
const req = httpTesting.expectOne(
{
method: 'GET',
url: '/api/config',
},
'Request to load the configuration',
);
ПОЛЕЗНО: API для проверки ожиданий сопоставляют полный URL запросов, включая параметры запроса.
Последний шаг — убедиться, что не осталось необработанных запросов — встречается достаточно часто, чтобы вынести его в шаг afterEach():
afterEach(() => {
// Verify that none of the tests make any extra HTTP requests.
TestBed.inject(HttpTestingController).verify();
}); Обработка нескольких запросов одновременно
Если в тесте нужно обработать повторяющиеся запросы, используйте API match() вместо expectOne(). Он принимает те же аргументы, но возвращает массив соответствующих запросов. После возврата эти запросы исключаются из дальнейшего сопоставления, и вы должны самостоятельно передать для них ответы и проверить их.
const allGetRequests = httpTesting.match({method: 'GET'});
for (const req of allGetRequests) {
// Handle responding to each request.
} Расширенное сопоставление
Все функции сопоставления принимают функцию-предикат для пользовательской логики сопоставления:
// Look for one request that has a request body. const requestsWithBody = httpTesting.expectOne((req) => req.body !== null);
Функция expectNone проверяет, что заданным критериям не соответствует ни один запрос.
// Assert that no mutation requests have been issued. httpTesting.expectNone((req) => req.method !== 'GET');
Тестирование обработки ошибок
Следует протестировать реакцию приложения на сбои HTTP-запросов.
Ошибки бэкенда
Чтобы протестировать обработку ошибок бэкенда (когда сервер возвращает код состояния, указывающий на неуспешное выполнение), передайте запросам ответы с ошибками, имитирующие ответы вашего бэкенда при сбое запроса.
const req = httpTesting.expectOne('/api/config');
req.flush('Failed!', {status: 500, statusText: 'Internal Server Error'});
// Assert that the application successfully handled the backend error. Сетевые ошибки
Запросы также могут завершаться с ошибкой из-за проблем с сетью, которые представлены ошибками ProgressEvent. Их можно передать с помощью метода error():
const req = httpTesting.expectOne('/api/config');
req.error(new ProgressEvent('network error!'));
// Assert that the application successfully handled the network error. Тестирование перехватчика
Следует проверить, что перехватчики работают в требуемых условиях.
Например, приложению может потребоваться добавлять к каждому исходящему запросу токен аутентификации, созданный сервисом. Это поведение можно обеспечить с помощью перехватчика:
export function authInterceptor(
request: HttpRequest<unknown>,
next: HttpHandlerFn,
): Observable<HttpEvent<unknown>> {
const authService = inject(AuthService);
const clonedRequest = request.clone({
headers: request.headers.append('X-Authentication-Token', authService.getAuthToken()),
});
return next(clonedRequest);
}
Конфигурация TestBed для этого перехватчика должна использовать функцию withInterceptors.
TestBed.configureTestingModule({
providers: [
AuthService,
// Testing one interceptor at a time is recommended.
provideHttpClient(withInterceptors([authInterceptor])),
provideHttpClientTesting(),
],
});
HttpTestingController позволяет получить экземпляр запроса, который затем можно проверить, чтобы убедиться, что запрос был изменён.
const service = TestBed.inject(AuthService);
const req = httpTesting.expectOne('/api/config');
expect(req.request.headers.get('X-Authentication-Token')).toEqual(service.getAuthToken());
Аналогичный перехватчик можно реализовать с помощью перехватчиков на основе классов:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private authService = inject(AuthService);
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const clonedRequest = request.clone({
headers: request.headers.append('X-Authentication-Token', this.authService.getAuthToken()),
});
return next.handle(clonedRequest);
}
}
Чтобы протестировать его, следует использовать следующую конфигурацию TestBed:
TestBed.configureTestingModule({
providers: [
AuthService,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
// We rely on the HTTP_INTERCEPTORS token to register the AuthInterceptor as an HttpInterceptor
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
],
});
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/http/testing