Jasmine toEqual失败但打印两个对象是相同的

时间:2017-02-16 03:17:26

标签: javascript jasmine jasmine2.0

In this Jasmine test我比较了几乎相同的两个对象,唯一的区别是第二个对象有一个额外的未定义成员。

describe('Testing', function () {

  it('should compare two objects', function () {


    var obj1 = {a: 1, b: 2 };

    var obj2 = {a: 1, b: 2, c: undefined };

    console.log(JSON.stringify(obj1));
    console.log(JSON.stringify(obj2));

    expect(obj1).toEqual(obj2);


  });

测试失败,但是使用JSON.stringify打印两个对象会产生两个相同的输出。

{"a":1,"b":2}
{"a":1,"b":2}

浏览对象可以找到差异,但在复杂对象中,这并不容易。关于如何处理这个的任何建议?

2 个答案:

答案 0 :(得分:1)

您的问题基于两个误解:

  1. obj2 c中是已定义的属性,其值为undefined
  2. stringify()未根据规范序列化undefined - 您的测试不安全
  3. 两个对象都是不等

    关于Jasmine toEqual()

    toEqual使用内部util.equals(),它将按键对比 a b中定义的所有可枚举键的对象键

    经过某些类型检查后,它会进入comparing the keys of the object

    关于定义属性

    看看ECMAscript规范。创建对象文字时会调用此内部Put方法:

      

    11.1.5 Object Initialiser

         

    [...]

         

    生产PropertyNameAndValueList:PropertyAssignment的计算方法如下:

         
        
    1. 让obj成为创建新对象的结果,就好像通过表达式new Object()一样,其中Object是具有该名称的标准内置构造函数。
    2.   
    3. 让propId成为评估PropertyAssignment的结果。
    4.   
    5. 使用参数propId.name,propId.descriptor和 false 调用obj的[[DefineOwnProperty]]内部方法。
    6.   
    7. 返回obj。
    8.         

      [...]

           

      生产PropertyAssignment:PropertyName:AssignmentExpression的计算方法如下:

           
          
      1. 让propName成为评估PropertyName的结果。
      2.   
      3. 让exprValue成为评估AssignmentExpression的结果。
      4.   
      5. 让propValue为GetValue(exprValue)。
      6.   
      7. 设desc为属性描述符{[[Value]]:propValue,[[Writable]]:true,[[Enumerable]]:true,[[Configurable]]:true}
      8.   

    通过成员表达式定义属性:

      

    8.12.5 [[Put]](P,V,投掷)   当使用属性P,值V和布尔标志Throw调用O的[[Put]]内部方法时,将执行以下步骤:

         

    [...]

         
        
    1. 否则,在对象O上创建一个名为P的命名数据属性,如下所示

           

      一个。设newDesc为属性描述符{[[Value]]:V,[[Writable]]:true,[[Enumerable]]:true,[[Configurable]]:true}。

           

      湾调用O的[[DefineOwnProperty]]内部方法,将P,newDesc和Throw作为参数传递。

    2.   

    8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)中描述的DefineOwnProperty的实施将不再重复。另外MDN says甚至默认值为undefined

    检查:

    > var obj2 = {a: 1, b: 2, c: undefined };
    > obj2.hasOwnProperty("c");
    < true
    

    stringify()

    查看ECMAscript spec on stringify()JSON spec

    Possible JSON values

    (来源:json.org)

    以下是ECMAscript规范的一部分:

      
        
    1. 否则   一个。设K是一个内部字符串列表,由[[Enumerable]]属性为true的所有值属性的名称组成。字符串的顺序应与Object.keys标准内置函数使用的顺序相同。
    2.   

    他们说,该对象的枚举属性应该符合Object.keys()的顺序(和结果)。让我们测试......

    > var obj2 = {a: 1, b: 2, c: undefined };
    > Object.keys(obj2);
    < ["a", "b", "c"]
    

    呃,他们是对的!

    然后有一个Str()函数,它定义了处理undefined值的行为。有几个If Type() ...步骤,不适用于未定义的值,以

    结尾
      
        
    1. 返回未定义
    2.   

    由对象序列化程序调用时:

      
        
    1. 对于K的每个元素P.

           

      一个。设strP是使用参数P和值调用抽象操作Str的结果。

           

      湾如果strP未定义

           

      [...]

    2.   

答案 1 :(得分:0)

正如评论和其中一个答案中所解释的,两个对象都是不相等的。

幸运的是,在Jasmine 2.5中,您可以使用Custom equality tester(定义自己的等号)解决此问题:

ID = -335362046
Closing handle 3959605250
There is not an open file with that identification number.

More help is available by typing NET HELPMSG 2314.

将此插入function customEquality(a, b) { let keys, key, equal = true; // Store unique list of keys over both objects keys = Object.keys(a).concat(Object.keys(b)).reduce(function(result, name) { if (!result.includes(name)) { result.push(name); } return result; }, []); for (key of keys) { // ignore when keys are defined in both objects, // having the value undefined if (typeof a[key] === "undefined" && a.hasOwnProperty(key) && typeof b[key] === "undefined" && b.hasOwnProperty(key)) { continue; } equal = equal && b[key] === a[key]; } return equal; } jasmine.addCustomEqualityTester(customEquality); 与测试相同的块中或实际beforeEach()内。

基本测试程序将忽略两个对象中存在的未定义值。如果一个对象中存在未定义的值,则不会将其视为相等。

请注意,此测试程序与纯it()的行为有很大不同,因为它不会比较Arrays或嵌套对象,也不会比较DOM节点等其他对象类型。这只是一个指导你的例子。