Тестирование сервисов
Сервисы обычно содержат бизнес-логику приложения, от которой зависят компоненты. Тестирование сервисов позволяет убедиться, что логика работает правильно изолированно, независимо от компонентов и шаблонов.
В этом руководстве используется Vitest, который входит в проекты Angular CLI по умолчанию. Подробнее о настройке тестирования см. в обзоре тестирования.
Тестирование сервиса
Рассмотрим сервис Calculator, выполняющий простые арифметические операции:
import {Service} from '@angular/core';
@Service()
export class Calculator {
add(a: number, b: number): number {
return a + b;
}
subtract(a: number, b: number): number {
return a - b;
}
}
Чтобы протестировать этот сервис, настройте TestBed — утилиту Angular для тестирования, которая создает изолированную среду для каждого теста. Она настраивает внедрение зависимостей и позволяет получать экземпляры сервисов, имитируя то, как Angular связывает компоненты в реальном приложении.
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, expect, it} from 'vitest';
import {Calculator} from './calculator';
describe('Calculator', () => {
let service: Calculator;
beforeEach(() => {
// Injects the Calculator service which is available to Angular
// because the service uses `providedIn: 'root'`
service = TestBed.inject(Calculator);
});
it('adds two numbers', () => {
expect(service.add(1, 2)).toBe(3);
});
it('subtracts two numbers', () => {
expect(service.subtract(5, 3)).toBe(2);
});
});
В приведенном выше примере блок beforeEach внедряет новый экземпляр сервиса перед каждым тестом. Это гарантирует, что каждый тест выполняется изолированно и состояние предыдущих тестов не сохраняется.
Тестирование сервисов с зависимостями
Для правильной работы большинству сервисов нужны другие сервисы. По умолчанию TestBed предоставляет реальные реализации этих зависимостей, поэтому тесты проверяют фактические участки кода, используемые приложением. Однако иногда зависимость может быть сложной, медленной или непредсказуемой. В таких случаях ее можно заменить управляемой подменой.
Рассмотрим сервис OrderTotal, который использует TaxCalculator для вычисления итоговой стоимости заказа:
import {Service} from '@angular/core';
@Service()
export class TaxCalculator {
calculate(subtotal: number): number {
return subtotal * 0.05;
}
}
import {inject, Service} from '@angular/core';
import {TaxCalculator} from './tax-calculator';
@Service()
export class OrderTotal {
private taxCalculator = inject(TaxCalculator);
total(subtotal: number): number {
return subtotal + this.taxCalculator.calculate(subtotal);
}
}
В этом примере OrderTotal использует inject(), чтобы запросить TaxCalculator у системы внедрения зависимостей Angular. По умолчанию TestBed предоставляет реальный TaxCalculator, что отлично подходит для таких простых вычислений. Однако если TaxCalculator содержит сложную логику, сетевые запросы или непредсказуемые результаты, его можно заменить управляемой подменой.
Замена зависимости заглушкой
Заглушка заменяет зависимость или метод реализацией, возвращающей предсказуемые значения, благодаря чему результаты тестов проще проверять.
Чтобы протестировать OrderTotal без использования настоящего TaxCalculator, можно указать заглушку в конфигурации TestBed.
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, expect, it, vi, type Mocked} from 'vitest';
import {OrderTotal} from './order-total';
import {TaxCalculator} from './tax-calculator';
// Vitest's `Mocked` utility type ensures the stub is type-safe,
// while `vi.fn()` creates a mock function for each method
const taxCalculatorStub: Mocked<TaxCalculator> = {
calculate: vi.fn(),
};
describe('OrderTotal', () => {
let service: OrderTotal;
beforeEach(() => {
// `mockReturnValue` sets a controlled return value for the stub
taxCalculatorStub.calculate.mockReturnValue(5);
TestBed.configureTestingModule({
// The `providers` array accepts a provider object where `provide`
// specifies the dependency to replace and `useValue` defines the stub
providers: [{provide: TaxCalculator, useValue: taxCalculatorStub}],
});
service = TestBed.inject(OrderTotal);
});
it('adds tax to the subtotal', () => {
expect(service.total(100)).toBe(105);
});
});
С этой заглушкой, когда OrderTotal запрашивает TaxCalculator, TestBed использует вместо него taxCalculatorStub. Поскольку заглушка всегда возвращает 5, тест проверяет, что OrderTotal правильно прибавляет налог к промежуточной сумме независимо от того, меняется ли ставка налога в TaxCalculator.
Проверка взаимодействия с помощью шпионов
Заглушка управляет значением, возвращаемым зависимостью, но иногда также нужно проверить, что сервис вызвал зависимость с правильными аргументами. Для этого можно использовать шпионы, отслеживающие вызовы функции. В Vitest эта возможность встроена в vi.fn() и позволяет проверять взаимодействие между сервисами.
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, expect, it, vi, type Mocked} from 'vitest';
import {OrderTotal} from './order-total';
import {TaxCalculator} from './tax-calculator';
const taxCalculatorStub: Mocked<TaxCalculator> = {
calculate: vi.fn(),
};
describe('OrderTotal', () => {
let service: OrderTotal;
beforeEach(() => {
taxCalculatorStub.calculate.mockReturnValue(5);
TestBed.configureTestingModule({
providers: [{provide: TaxCalculator, useValue: taxCalculatorStub}],
});
service = TestBed.inject(OrderTotal);
});
it('adds tax to the subtotal', () => {
expect(service.total(100)).toBe(105);
});
// Verify the interaction with a spy
it('calls the tax calculator', () => {
service.total(100);
expect(taxCalculatorStub.calculate).toHaveBeenCalledExactlyOnce();
});
});
Новый тест проверяет, что OrderTotal вызвал TaxCalculator.calculate при вычислении общей суммы. Это полезно, когда нужно убедиться, что взаимодействие между сервисами произошло правильно.
Тестирование HTTP-сервисов
Многие сервисы используют HttpClient Angular для получения данных с сервера. Angular предоставляет специальные средства тестирования для HttpClient, позволяющие управлять HTTP-ответами без выполнения реальных сетевых запросов.
Подробнее о тестировании сервисов, использующих HttpClient, см. в руководстве по тестированию HTTP.
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/testing/services