如何使用 Jasmine 监视值属性(而不是方法)

Posted

技术标签:

【中文标题】如何使用 Jasmine 监视值属性(而不是方法)【英文标题】:How to spyOn a value property (rather than a method) with Jasmine 【发布时间】:2014-01-19 17:25:57 【问题描述】:

Jasmine 的spyOn 可以很好地改变方法的行为,但是有没有办法改变对象的值属性(而不是方法)?代码如下:

spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);

【问题讨论】:

【参考方案1】:

2017 年 2 月,他们合并了一个 PR,添加了此功能,并于 2017 年 4 月发布。

所以要监视您使用的 getter/setter: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); 其中 myObj 是您的实例,“myGetterName”是您的类中定义为 get myGetterName() 的实例的名称,第三个参数是 getset 的类型。

您可以使用与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 源代码中使用此方法。

https://github.com/jasmine/jasmine/blob/7f8f2b5e7a7af70d7f6b629331eb6fe0a7cb9279/src/core/requireInterface.js#L199

而 spyOnProperty 方法是here

使用 jasmine 2.6.1 回答原始问题,您将:

const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

【讨论】:

如果valueAObservableSubject,我该怎么做?我收到Property valueA does not have access type get 这可能意味着您不能在该属性上使用它。 spyOnProperty 正在使用 getOwnPropertyDescriptor 并检查该属性描述符的访问类型 get|set 是否存在。您收到的错误是因为未设置属性描述符或未为您尝试监视的属性设置或获取。 Jasmine 这样做: const descriptor = Object.getOwnPropertyDescriptor ... if(!descriptor[accessType]) // 抛出错误 那么,如果没有明确的 getter 和 setter 方法,就没有办法窥探属性吗?间谍是使用的属性。 @Dominik 在这里,我在一篇较长的文章中写下了与此相关的所有幕后内容:medium.com/@juanlizarazo/… @Mathieu 这不是一个好的断言,因为它只是断言我们已经从我们告诉间谍要做的事情中知道的事情,但这正是他们所要求的以及他们相同的问题代码 sn-p ¯_(ツ)_/¯ 他们的问题的重点是如何监视财产,而不是他们的具体例子。【参考方案2】:

有什么理由不能直接在对象上更改它?并不是说 javascript 强制对对象的属性进行可见性。

【讨论】:

using spyOn 明确表示我想模拟一些东西,而我直接设置属性隐含表示我想模拟一些东西,我不确定其他人会明白我在模拟一些东西时他正在阅读代码。另一种情况是我不想改变对象的内部行为,例如如果我改变数组的长度属性,数组会被修剪,所以模拟会更好 @Shuping,在这种情况下,您不会在嘲笑。你会存根,这完全没问题。您只会在测试中“改变行为”,这是您尝试使用 spyOn 实现的目标。 您可能想监视运行时对象原型以确保该属性将在运行时存在。如果属性不存在,则 Jasmine spyOn 未通过测试。 一个例子是设置或删除window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>' 在javascript中重新分配对象属性并不总是可以的【参考方案3】:

Jasmine 没有该功能,但您可以使用 Object.defineProperty 一起破解某些东西。

您可以重构代码以使用 getter 函数,然后监视 getter。

spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);

【讨论】:

【参考方案4】:

最好的方法是使用spyOnProperty。它需要 3 个参数,您需要将 getset 作为第三个参数传递。

示例

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);

这里我设置了getclientWidthdiv.nativeElement 对象。

【讨论】:

【参考方案5】:

正确的做法是使用 spy on 属性,它可以让您模拟具有特定值的对象的属性。

const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

【讨论】:

【参考方案6】:

如果您使用的是 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');

【讨论】:

或者,如果 getter 是被测类的一部分,则可以将存根注入子类中。【参考方案7】:

假设有这样的方法需要测试 小图的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 名称

【讨论】:

【参考方案8】:

我正在使用剑道网格,因此无法将实现更改为 getter 方法,但我想围绕这个进行测试(模拟网格)而不是测试网格本身。我使用的是间谍对象,但这不支持属性模拟,所以我这样做:

    this.$scope.ticketsGrid =  
        showColumn: jasmine.createSpy('showColumn'),
        hideColumn: jasmine.createSpy('hideColumn'),
        select: jasmine.createSpy('select'),
        dataItem: jasmine.createSpy('dataItem'),
        _data: []
     

虽然有点啰嗦,但很管用

【讨论】:

【参考方案9】:

我知道我在这里聚会有点晚了,但是,

您可以直接访问调用对象,它可以为您提供每次调用的变量

expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)

【讨论】:

【参考方案10】:

您不能模拟变量,但可以为其创建 getter 函数并在您的规范文件中模拟该方法。

【讨论】:

以上是关于如何使用 Jasmine 监视值属性(而不是方法)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jasmine 监视私有变量的属性/函数

jasmine toHaveBeenCalledWith 部分匹配

如何对 DOM 操作进行单元测试(使用 jasmine)

使用 Jasmine 监视 Backbone.js 路由调用

使用 Jasmine 监视没有对象的函数

如何在 Jasmine 1 中监视 Falcor 数据模型构造函数