【发布时间】:2014-01-19 17:25:57
【问题描述】:
Jasmine 的spyOn 可以很好地改变方法的行为,但是有没有办法改变对象的值属性(而不是方法)?代码如下:
spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);
【问题讨论】:
标签: javascript unit-testing mocking jasmine
Jasmine 的spyOn 可以很好地改变方法的行为,但是有没有办法改变对象的值属性(而不是方法)?代码如下:
spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);
【问题讨论】:
标签: javascript unit-testing mocking jasmine
2017 年 2 月,他们合并了一个 PR,添加了此功能,并于 2017 年 4 月发布。
所以要监视您使用的 getter/setter:
const spy = spyOnProperty(myObj, 'myGetterName', 'get');
其中 myObj 是您的实例,“myGetterName”是您的类中定义为 get myGetterName() {} 的实例的名称,第三个参数是 get 或 set 的类型。
您可以使用与spyOn 创建的间谍相同的断言。
所以你可以例如:
const spy = spyOnProperty(myObj, 'myGetterName', 'get'); // to stub and return nothing. Just spy and stub.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.returnValue(1); // to stub and return 1 or any value as needed.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.callThrough(); // Call the real thing.
如果您有兴趣,可以在 github 源代码中使用此方法。
而 spyOnProperty 方法是here
使用 jasmine 2.6.1 回答原始问题,您将:
const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
【讨论】:
valueA 是Observable 或Subject,我该怎么做?我收到Property valueA does not have access type get
有什么理由不能直接在对象上更改它?并不是说 javascript 强制对对象的属性进行可见性。
【讨论】:
spyOn 明确表示我想模拟一些东西,而我直接设置属性隐含表示我想模拟一些东西,我不确定其他人会明白我在模拟一些东西时他正在阅读代码。另一种情况是我不想改变对象的内部行为,例如如果我改变数组的长度属性,数组会被修剪,所以模拟会更好
spyOn 实现的目标。
spyOn 未通过测试。
TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Jasmine 没有该功能,但您可以使用 Object.defineProperty 一起破解某些东西。
您可以重构代码以使用 getter 函数,然后监视 getter。
spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);
【讨论】:
最好的方法是使用spyOnProperty。它需要 3 个参数,您需要将 get 或 set 作为第三个参数传递。
const div = fixture.debugElement.query(By.css('.ellipsis-overflow'));
// now mock properties
spyOnProperty(div.nativeElement, 'clientWidth', 'get').and.returnValue(1400);
spyOnProperty(div.nativeElement, 'scrollWidth', 'get').and.returnValue(2400);
这里我设置了get 的clientWidth 的div.nativeElement 对象。
【讨论】:
正确的做法是使用 spy on 属性,它可以让您模拟具有特定值的对象的属性。
const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
【讨论】:
如果您使用的是 ES6 (Babel) 或 TypeScript,您可以使用 get 和 set 访问器来存根属性
export class SomeClassStub {
getValueA = jasmine.createSpy('getValueA');
setValueA = jasmine.createSpy('setValueA');
get valueA() { return this.getValueA(); }
set valueA(value) { this.setValueA(value); }
}
然后在您的测试中,您可以检查该属性是否设置为:
stub.valueA = 'foo';
expect(stub.setValueA).toHaveBeenCalledWith('foo');
【讨论】:
假设有这样的方法需要测试
小图的src属性需要检查
function reportABCEvent(cat, type, val) {
var i1 = new Image(1, 1);
var link = getABC('creosote');
link += "&category=" + String(cat);
link += "&event_type=" + String(type);
link += "&event_value=" + String(val);
i1.src = link;
}
下面的 spyOn() 会导致“新图像”被输入来自测试的假代码 spyOn 代码返回一个只有 src 属性的对象
由于变量“hook”的范围是在 SpyOn 中的假代码中可见,并且稍后在调用“reportABCEvent”之后也可见
describe("Alphabetic.ads", function() {
it("ABC events create an image request", function() {
var hook={};
spyOn(window, 'Image').andCallFake( function(x,y) {
hook={ src: {} }
return hook;
}
);
reportABCEvent('testa', 'testb', 'testc');
expect(hook.src).
toEqual('[zubzub]&arg1=testa&arg2=testb&event_value=testc');
});
这适用于 jasmine 1.3,但如果“andCallFake”更改为,则可能适用于 2.0 2.0 名称
【讨论】:
我正在使用剑道网格,因此无法将实现更改为 getter 方法,但我想围绕这个进行测试(模拟网格)而不是测试网格本身。我使用的是间谍对象,但这不支持属性模拟,所以我这样做:
this.$scope.ticketsGrid = {
showColumn: jasmine.createSpy('showColumn'),
hideColumn: jasmine.createSpy('hideColumn'),
select: jasmine.createSpy('select'),
dataItem: jasmine.createSpy('dataItem'),
_data: []
}
虽然有点啰嗦,但很管用
【讨论】:
我知道我在这里聚会有点晚了,但是,
您可以直接访问调用对象,它可以为您提供每次调用的变量
expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)
【讨论】:
您不能模拟变量,但可以为其创建 getter 函数并在您的规范文件中模拟该方法。
【讨论】: