jasmine.createSpyObj 具有属性
jasmine.createSpyObj with properties
在我的 Angular 测试中模拟依赖项时,我通常使用 jasmine.createSpyObj
:
创建一个间谍对象
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
然后将其提供给 TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
当我在测试中使用它时,我可以指定所需的 return 值:
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;
我能让它 'half' 工作的唯一方法是:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
这里的问题是它在创建时是一次性的。如果我想在测试中更改期望值,它不起作用。
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
是否存在通过创建间谍对象来解决模拟方法和属性的解决方案,或者我是否应该创建自己的假 class 然后在其上使用 spyOn
和 spyOnProperty
?
我也想知道createSpyObj
定义中的properties数组有什么用。到目前为止,我还没有在网上看到任何解释它的例子。
根据 the documentation(强调我的):
You can create a spy object with several properties on it quickly by
passing an array or hash of properties as a third argument to
createSpyObj
. In this case you won’t have a reference to the created
spies, so if you need to change their spy strategies later, you will
have to use the Object.getOwnPropertyDescriptor
approach.
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
on MDN),因此要访问间谍对象,您需要获取描述符对象然后与 get
和 set
定义的方法。
在 TypeScript 中,编译器需要一点帮助。 createSpyObj
returns 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
};
因此,要访问描述符 getter 上的 .and
,您需要 optional chaining (as Object.getOwnPropertyDescriptor
may return undefined
) and a type assertion to a Spy
:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
非常感谢@jonrsharpe!我刚刚添加了一个函数,所以我可以这样做:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
定义为:
function spyPropertyGetter<T, K extends keyof T>(
spyObj: jasmine.SpyObj<T>,
propName: K
): jasmine.Spy<() => T[K]> {
return Object.getOwnPropertyDescriptor(spyObj, propName)?.get as jasmine.Spy<() => T[K]>;
}
这个也能完成这项工作:)
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
Object.getOwnPropertyDescriptor(serviceSpy, "method").value.and.returnValue("test");
感谢 post,非常有帮助。
我添加了一个 Stackblitz Demo (Angular v12) 到 [=v15=]
乔恩·夏普的 & s.alems 解决方案
在我的 Angular 测试中模拟依赖项时,我通常使用 jasmine.createSpyObj
:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
然后将其提供给 TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
当我在测试中使用它时,我可以指定所需的 return 值:
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;
我能让它 'half' 工作的唯一方法是:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
这里的问题是它在创建时是一次性的。如果我想在测试中更改期望值,它不起作用。
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
是否存在通过创建间谍对象来解决模拟方法和属性的解决方案,或者我是否应该创建自己的假 class 然后在其上使用 spyOn
和 spyOnProperty
?
我也想知道createSpyObj
定义中的properties数组有什么用。到目前为止,我还没有在网上看到任何解释它的例子。
根据 the documentation(强调我的):
You can create a spy object with several properties on it quickly by passing an array or hash of properties as a third argument to
createSpyObj
. In this case you won’t have a reference to the created spies, so if you need to change their spy strategies later, you will have to use theObject.getOwnPropertyDescriptor
approach.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
on MDN),因此要访问间谍对象,您需要获取描述符对象然后与 get
和 set
定义的方法。
在 TypeScript 中,编译器需要一点帮助。 createSpyObj
returns 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
};
因此,要访问描述符 getter 上的 .and
,您需要 optional chaining (as Object.getOwnPropertyDescriptor
may return undefined
) and a type assertion to a Spy
:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
非常感谢@jonrsharpe!我刚刚添加了一个函数,所以我可以这样做:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
定义为:
function spyPropertyGetter<T, K extends keyof T>(
spyObj: jasmine.SpyObj<T>,
propName: K
): jasmine.Spy<() => T[K]> {
return Object.getOwnPropertyDescriptor(spyObj, propName)?.get as jasmine.Spy<() => T[K]>;
}
这个也能完成这项工作:)
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
Object.getOwnPropertyDescriptor(serviceSpy, "method").value.and.returnValue("test");
感谢 post,非常有帮助。
我添加了一个 Stackblitz Demo (Angular v12) 到 [=v15=] 乔恩·夏普的 & s.alems 解决方案