jasmine.createSpyObj со свойствами
Когда я высмеиваю зависимости в моих тестах Angular, я обычно создаю объект-шпион, используя jasmine.createSpyObj:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
затем предоставьте его TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
Когда я использую его в своем тесте, я могу указать желаемое возвращаемое значение:
serviceSpy.method.and.returnValue(of([...]));
Теперь мне также нужно имитировать свойства, и я не могу понять, как это сделать. createSpyObjпозволяет определять имена свойств:
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);
но я безуспешно пробовал различные решения, основанные на многочисленных статьях и ответах, например:
// Cannot read property 'and' of undefined
serviceSpy.property.and.returnValue(true);
// not declared configurable
spyOnProperty(serviceSpy, 'property').and.returnValue(true);
// no build errors, but value stays 'undefined'
serviceSpy.property = true;
Единственный способ сделать это "наполовину" - это:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
Проблема здесь в том, что это одноразовый набор при создании. Если я хочу изменить ожидаемое значение в тесте, это не сработает.
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
Существует ли решение как для имитационных методов, так и для свойств путем создания шпионского объекта, или я должен создать свой собственный поддельный класс, на котором я затем могу использовать spyOnи spyOnProperty?
Я также хотел бы знать, как используется массив свойств в createSpyObjопределении. Пока я не видел в сети примеров, объясняющих это.
Ответы
В документации (выделено мной):
Вы можете быстро создать объект-шпион с несколькими свойствами, передав массив или хэш свойств в качестве третьего аргумента
createSpyObj. В этом случае у вас не будет ссылки на созданных шпионов, поэтому, если вам понадобится изменить их шпионские стратегии позже, вам придется использовать этотObject.getOwnPropertyDescriptorподход .it("creates a spy object with properties", function() { let obj = createSpyObj("myObject", {}, { x: 3, y: 4 }); expect(obj.x).toEqual(3); Object.getOwnPropertyDescriptor(obj, "x").get.and.returnValue(7); expect(obj.x).toEqual(7); });
Подсмотрел свойства дескрипторы (смотри , например , Object.definePropertyна MDN ), поэтому доступ шпион объекты , которые вы должны получить объект дескриптора затем взаимодействуют с getи setметодами , определенными на нем.
В TypeScript компилятору нужна небольшая помощь. createSpyObjвозвращает либо anyили SpyObj<T>, а SpyObjтолько определяет методы , за которыми ведется слежка:
type SpyObj<T> = T & {
[K in keyof T]: T[K] extends Func ? T[K] & Spy<T[K]> : T[K];
// | if it's a | spy on it | otherwise leave
// | callable | | it alone
};
Итак, чтобы получить доступ .andк геттеру дескриптора, вам понадобится дополнительная цепочка (которая Object.getOwnPropertyDescriptorможет возвращать undefined) и утверждение типа для Spy:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
Игровая площадка
Большое спасибо @jonrsharpe! Я просто добавил функцию, поэтому могу:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
Что определяется как:
function spyPropertyGetter(
spyObj: jasmine.SpyObj<any>,
propName: string
) {
return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
jasmine.Func
>;
}