Перейти к содержимому

returns или overrides ​

Обе опции задают значения спая заранее, в момент создания. Тесту не нужен beforeEach, полный mockReturnValue. returns — для метода: метод остаётся спаем. overrides — для всего остального (сигнал, поток, поле): член становится ровно тем значением, которое вы передали.

ts
import { signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { injectSpy, provideAutoSpy } from 'vitest-auto-spy/angular';

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [LayoutComponent],
    providers: [
      provideAutoSpy(LayoutService, {
        returns: { load: of([]) }, // load() остаётся спаем и отвечает of([])
        overrides: { isCompact: signal(true) }, // isCompact — этот сигнал, а не спай
      }),
    ],
  });
});

it('loads the layout once', () => {
  TestBed.createComponent(LayoutComponent).detectChanges();
  expect(injectSpy(LayoutService).load).toHaveBeenCalledOnce();
});

Обе опции есть у каждой фабрики: createSpyFromClass, provideAutoSpy, provideAutoSpyForToken и createAutoMock. Обе настраивают один и тот же спай: каждый ключ задаёт один его член.

Бок о бок ​

returnsoverrides
Что принимаетимя методалюбой член: поле, сигнал, свойство-Observable, объект конфигурации
Значение — этото, что отвечает методсам член
Потомпо-прежнему спай; toHaveBeenCalled работаетзначение как написано, не спай
То же, что написатьspy.m.mockReturnValue(x) в beforeEachspy.p = x в beforeEach
Настройка позжеболее поздний calledWith, mockReturnValue или resolveWith заменяет это значениенастраивать нечего; присвойте новое значение
Под strict (метод без настройки бросает ошибку)метод настроен, даже если значение undefinedметод настроен, если для него передана функция
Ключ, который не методпри создании спая выводится предупреждение: значение никогда не вернётсятак и задумано: overrides ровно для этого

strict — опция, с которой метод спая бросает ошибку, если тест вызвал его, ничего не настроив. См. Строгий режим.

Что выбрать ​

Метод — в returns. Всё остальное — в overrides. Если метод возвращает объект другого класса, который тест тоже настраивает, назовите класс в returnsClass, а не стройте этот спай сами. Доставайте этот спай через innerDouble(double, 'method'): он не вызывает метод, поэтому returnsClass подходит и тесту, который считает вызовы метода.

ts
import { signal } from '@angular/core';
import { Subject, of } from 'rxjs';
import { injectSpy, provideAutoSpy } from 'vitest-auto-spy/angular';

// метод: его по-прежнему можно проверить
provideAutoSpy(CartService, { returns: { total: 0, load: of([]) } });
expect(injectSpy(CartService).load).toHaveBeenCalledOnce();

// состояние: сигнал, поток, поле
provideAutoSpy(LayoutService, { overrides: { isCompact: signal(true), resize$: new Subject<void>() } });

Частая ошибка: писать returns: { m: undefined } без strict. Ненастроенный метод и так отвечает undefined, поэтому без strict такая строка ничего не меняет. С strict всё иначе: там эта строка нужна, чтобы вызов m() не бросал ошибку.

Функция в overrides ​

Метод кладут в overrides, когда его ответ зависит от аргументов. В createSpyFromClass и provideAutoSpy переданная функция остаётся спаем. Она работает как реализация метода, и вызовы по-прежнему записываются:

ts
import { DomSanitizer } from '@angular/platform-browser';
import { injectSpy, provideAutoSpy } from 'vitest-auto-spy/angular';

provideAutoSpy(DomSanitizer, { overrides: { sanitize: (_context, value) => String(value) } });
expect(injectSpy(DomSanitizer).sanitize).toHaveBeenCalledOnce();

Это касается createSpyFromClass и provideAutoSpy. createAutoMock и provideAutoSpyForToken устроены иначе: они строят спай по типу TypeScript, а не по классу. Во время выполнения класса нет, и отличить метод от свойства им не по чему. Поэтому там обычная функция сохраняется как написана и спаем не становится. vi.fn() сохраняется как есть в любой фабрике.

Член, названный в обеих ​

Побеждает overrides. returns этот член пропускает, что бы ни лежало в overrides: обычное значение, функция или vi.fn().

То же правило действует между зарегистрированным значением по умолчанию и вызовом, который создаёт спай: значение из вызова заменяет зарегистрированные returns или selfReturning (список методов, которые возвращают сам спай, для цепочек вызовов).

Опубликовано под лицензией MIT.