问题描述
在我的 Angular 测试中模拟依赖项时,我通常使用 jasmine.createSpyObj
创建一个间谍对象:
When mocking dependencies in my Angular tests, I usually create a spy object using jasmine.createSpyObj
:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
然后将其提供给 TestBed:
then provide it to the TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
当我在测试中使用它时,我可以指定所需的返回值:
When I use it in my test, I can then specify the desired return value:
serviceSpy.method.and.returnValue(of([...]));
现在我还需要模拟属性,但我不知道应该怎么做.createSpyObj
确实允许定义属性名称:
Now I also need to mock properties and I cannot find out how it should be done. createSpyObj
does allow the definition of property names:
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);
但我根据大量文章和答案尝试了不同的解决方案,但没有任何成功,例如:
but I've tried varies solutions based on the numerous articles and answers out there without any success, e.g.:
// 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;
我可以让它工作一半"的唯一方法是:
The only way I could make it 'half' work is:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
这里的问题是它在创建时是一次性的.如果我想更改测试中的期望值,它不起作用.
The problem here is that it's a one-time set at creation. If I want to change the expected value in the test, it does not work.
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
是否存在通过创建 spy 对象来模拟方法和属性的解决方案,或者我应该创建自己的假类,然后我可以在其上使用 spyOn
和 spyOnProperty
?
Does there exist a solution to both mock methods and properties by creating a spy object, or should I create my own fake class on which I can then use spyOn
and spyOnProperty
?
我还想知道 createSpyObj
定义中的属性数组的用途.到目前为止,我还没有在网上看到任何解释它的例子.
I would also like to know what the usage is of the properties array in the createSpyObj
definition. So far I have not seen any example on the web that explains it.
推荐答案
根据 文档 (强调我的):
Per the documentation (emphasis mine):
您可以通过以下方式快速创建一个具有多个属性的间谍对象将属性数组或散列作为第三个参数传递给createSpyObj
.在这种情况下,您将不会引用创建的间谍,所以如果你以后需要改变他们的间谍策略,你会必须使用 Object.getOwnPropertyDescriptor
方法.
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
方法定义就可以了.
Spied properties are descriptors (see e.g. Object.defineProperty
on MDN), so to access the spy objects you need to get the descriptor object then interact with the get
and set
methods defined on it.
在 TypeScript 中,编译器需要一些帮助.createSpyObj
返回 any
或 SpyObj
,而 SpyObj
仅将 methods 定义为被监视上:
In TypeScript, the compiler needs a bit of help. createSpyObj
returns either any
or SpyObj<T>
, and a SpyObj
only defines the methods as being spied on:
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
,您需要 可选链接(因为 Object.getOwnPropertyDescriptor
可能返回 undefined
)和一个 类型断言到 Spy
:
So to access .and
on the descriptor's getter, you'll need 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);
游乐场
这篇关于jasmine.createSpyObj 与属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!