在NodeJS中测试对象的相等性

24

我们正在为一个程序编写测试。我们想编写一个功能性测试来验证程序的输出是否符合预期。

返回的对象是一个复杂的JS对象(包含嵌套对象、多个属性等)。

我们希望测试它是否与我们需要的一致。

到目前为止,我们一直在“浏览”对象和预期结果,逐个检查每个属性和每个嵌套对象。这非常繁琐,我们想知道是否有任何库可以基于对象“构建”所有的测试。例如这样的东西。

var res = {
  a: {
    alpha: [1,2,3],
    beta: "Hello",
    gamma: "World"
    },
    },
    b: 123,
    c: "It depends"
  }
};
var expectation = {
  a: {
    alpha: [1,2,4],
    beta: "Hello",
    gamma: "World"
    },
    },
    b: 123,
    c: "It depends"
  }
};

assert(res, expectation) // -> Raises an error because res[a][b][2] is different from expectation[a][b][2].
在这个例子中,我简化了我们对象的复杂性......

我应该坚持这样一个事实:我们需要一段聪明到足以告诉我们什么是不同的代码,而不仅仅告诉我们这两个对象是不同的。我们现在知道深度相等,但我们还没有找到任何可以告诉我们差异的东西。


http://docs.jquery.com/QUnit/deepEqual - Fabrício Matté
http://documentcloud.github.com/underscore/#isEqual - JohnnyHK
6个回答

25

Node具有内置的assert模块,用于测试。它有一个名为deepEqual的方法,用于深度相等性检查。

函数签名如下:

assert.deepEqual(actual, expected, [message])

测试深度相等并返回差异的快速编写函数:

// Will test own properties only
function deepEqualWithDiff(a, e, names){
  var dif = {};
  var aKeys = Object.keys(a);
  var eKeys = Object.keys(e);

  var cKeys = aKeys;
  var dKeys = eKeys;
  var c = a;
  var d = e;
  var names = {
    c: names ? names['a'] : 'Actual',
    d: names ? names['e'] : 'Expected'
  }

  if(eKeys.length > aKeys.length){
    cKeys = eKeys;
    dKeys = aKeys;
    c = e;
    d = a;
    names = {
      d: names ? names['a'] : 'Actual',
      c: names ? names['e'] : 'Expected'
    }
  }


  for(var i = 0, co = cKeys.length; i < co; i++){
    var key = cKeys[i];
    if(typeof c[key] !== typeof d[key]){
      dif[key] = 'Type mismatch ' + names['c'] + ':' + typeof c[key] + '!==' + names['d'] + typeof d[key];
      continue;
    }
    if(typeof c[key] === 'function'){
      if(c[key].toString() !== d[key].toString()){
        dif[key] = 'Differing functions';
      }
      continue;
    }
    if(typeof c[key] === 'object'){
      if(c[key].length !== undefined){ // array
        var temp = c[key].slice(0);
        temp = temp.filter(function(el){
          return (d[key].indexOf(el) === -1);
        });
        var message = '';
        if(temp.length > 0){
          message += names['c'] + ' excess ' + JSON.stringify(temp);
        }

        temp = d[key].slice(0);
        temp = temp.filter(function(el){
          return (c[key].indexOf(el) === -1);
        });
        if(temp.length > 0){
          message += ' and ' + names['d'] + ' excess ' + JSON.stringify(temp);
        }
        if(message !== ''){
          dif[key] = message;
        }
        continue;
      }
      var diff = deepEqualWithDiff(c[key], d[key], {a:names['c'],e:names['d']});
      if(diff !== true && Object.keys(diff).length > 0){
        dif[key] = diff;
      }
      continue;
    }
    // Simple types left so
    if(c[key] !== d[key]){
      dif[key] = names['c'] + ':' + c[key] + ' !== ' + names['d'] + ':' + d[key]; 
    }
  }
  return Object.keys(dif).length > 0 ? dif : true;
}

1
这并不是我们正在寻找的,因为它只是表明这两个对象不相等...但未告诉我们有什么不同! - Julien Genestoux
3
这有点不幸,因为assert的“deepequal”不能像常规的“isdeepequal”一样使用(似乎没有例外)。 - Steven Lu

18

JavaScript本身不支持深度比较对象,我也不知道NodeJS API中是否有任何内置的内容。

我的建议可能会是使用 Underscore 和它的 isEqual 函数。

npm install underscore

var _ = require('underscore');
var moe   = {name : 'moe', luckyNumbers : [13, 27, 34]};
var clone = {name : 'moe', luckyNumbers : [13, 27, 34]};
moe == clone;
=> false
_.isEqual(moe, clone);
=> true

虽然大多数Node测试框架应该也包含对象深度相等的断言,但您没有提到您正在使用哪一个。


1
这并不是我们正在寻找的,因为它只是表明这两个对象不相等...但未告诉我们有什么不同! - Julien Genestoux
好的,谢谢指出。这里有一个使用下划线的差异算法:http://blog.vjeux.com/2011/javascript/object-difference.html - Daff

6

deep-diff可以完成OP所需的功能。它可以比较两个JavaScript对象/JSON对象,并以您的代码可以访问的方式列出差异。

这是一个老问题,所以在问题被提出时可能不存在这个库。


3
注意,自Node.js v9.9.0起,Node的deepEqual()已被废弃,请使用deepStrictEqual()代替:
const assert = require('assert');

const actual = {
  a: {
    name: 'some',
    b: {
      name: 'thing',
    },
  },
};

const expected = {
  a: {
    name: 'thing',
    b: {
      name: 'some',
    },
  },
};

assert.deepStrictEqual(actual, expected, 'Expect names to be equal.');

专业小技巧: 如果您在deepStrictEqual中没有使用自定义消息,您将会获得两个对象之间的差异的漂亮且丰富多彩的输出结果:

enter image description here


1
虽然我认为对象比较的基础已经被讨论过了(参见这里和其他内容),但如果您正在寻找一种快速而简单的方法来执行这些测试并查看两个不相等对象之间的差异,则可以在Webstorm IDE中使用nodeunit运行测试(这里)。
Webstorm与nodeunit集成特别好,对于任何test.equals()或test.deepEquals()断言,它都提供了可视化的差异,并突出显示您的差异。我强烈推荐这个IDE,因为它将测试很好地集成到我的js开发周期中。
现在,如果您需要该测试/差异的结果在您的代码中可访问,那么这显然是不够的,我建议从我列出的第一个链接中复制几个deepEquals比较器。
祝你好运,
布莱恩

0

这是我使用 Lodash 编写的一个简单/灵活的深度差异函数:

_.merge(obj1, obj2, function (objectValue, sourceValue, key, object, source) {
    if ( !(_.isEqual(objectValue, sourceValue)) && (Object(objectValue) !== objectValue)) {
        console.log(key + "\n    Expected: " + sourceValue + "\n    Actual: " + objectValue);
    }
});

您可以将console.log()语句替换为您需要的任何比较逻辑。我的只是将差异打印到控制台上。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接