简体   繁体   English

使用QUnit(或其他单元测试工具)测试地图/集

[英]Testing Maps/Sets with QUnit (or other Unit Testing Tool)

How do we assert for equality of ES6 Maps and Sets? 我们如何断言ES6映射和集合是否相等?

For example: 例如:

// ES6 Map
var m1 = new Map();
m1.set('one', 1);
var m2 = new Map();
m2.set('two', 2);
assert.deepEqual(m1,m2);     // outputs: passed.

// ES6 Set
var s1 = new Set();
s1.add(1);
var s2 = new Set();
s2.add(2);
assert.deepEqual(s1,s2);     // outputs: passed.

The intention is to assert that the elements of the Sets/Maps are equal. 目的是断言集合/映射的元素相等。 Both the both assertions should fail. 这两个断言都应该失败。

Is there an equivalent of deepEqual for Sets/Maps? 集/地图是否具有deepEqual的等效项? In other words, short of manually iterating the elements, how do we test for Set/Map equality deeply? 换句话说,除了手动迭代元素外,我们如何深入测试Set / Map的相等性?

If there is no way in QUnit, is there a unit testing tool that works for ES6 Sets and Maps? 如果QUnit中没有办法,是否有适用于ES6集和地图的单元测试工具?

Edit 编辑

In Firefox, which supports Array.from() , I have been comparing sets and maps by: 在支持Array.from() Firefox中,我一直在通过以下方式比较集合和映射:

assert.deepEqual(Array.from(m1), Array.from(m2));

But this does not work with other browsers, which do not support Array.from() . 但这不适用于其他不支持Array.from()浏览器。 Even with an Array.from polyfill, Chrome / IE does not work - Array.from(set) always produces an empty array regardless of the set contents. 即使使用Array.from ,Chrome / IE也不起作用Array.from(set)始终生成一个空数组,无论设置内容如何。 This is possibly due to these browsers' lack of support for generic iterables. 这可能是由于这些浏览器缺乏对通用可迭代对象的支持。

Secondly, reducing it into a comparison of Arrays may not be always appropriate. 其次,将其简化为数组比较可能并不总是合适的。 We would end up with what I consider to be false positives: 我们最终会得出我认为是误报的信息:

var s = new Set();
s.add([1,2]);
var m = new Map();
m.set(1,2);
assert.deepEqual(Array.from(s), Array.from(m));  // outputs: passed.

Update : 更新

A patch is currently in the works at QUnit to extend deepEqual() to handle ES6 Sets and Maps. QUnit当前正在开发一个补丁程序,以扩展deepEqual()来处理ES6集和地图。 When that pull request gets merged in, we should be able to use deepEqual() to compare Sets and Maps. 当合并请求请求时,我们应该能够使用deepEqual()比较Set和Maps。 (-: (-:

Exhaustive Map comparison using higher-order functions 使用高阶函数进行详尽的地图比较

I'm going to approach this the same way I approached array comparison in this similar answer: How to compare arrays in JavaScript? 我将以与在类似的答案中进行数组比较的相同方式进行处理: 如何在JavaScript中比较数组?

I'm going to go thru the code bit-by-bit, but I'll have a complete runnable example at the end 我将逐位检查代码,但最后我将有一个完整的可运行示例


Shallow comparison 比较浅

First off, we're going to start with a generic Map comparison function. 首先,我们将从通用的Map比较功能开始。 This way we can do all sorts of comparisons on Map objects, not just testing for equality 这样,我们可以对Map对象进行各种比较,而不仅仅是测试是否相等

This mapCompare function agrees with our intuition about how Maps should be compared - we compare each key from m1 against each key from m2 . 这个mapCompare函数与我们关于如何比较Map的直觉相mapCompare -我们将m1中的每个键与m2中的每个键进行比较。 Note, this specific comparator is doing shallow comparison. 注意,这个特定的比较器正在做浅层比较。 We'll handle deep comparison in just a moment 稍后我们将进行深度比较

const mapCompare = f => (m1, m2) => {
  const aux = (it, m2) => {
    let {value, done} = it.next()
    if (done) return true
    let [k, v] = value
    return f (v, m2.get(k), $=> aux(it, m2))
  }
  return aux(m1.entries(), m2) && aux(m2.entries(), m1)
}

The only thing that might not be immediately clear is the $=> aux(it, m2) thunk. 唯一可能无法立即清除的是$=> aux(it, m2) thunk。 Maps have a built-in generator, .entries() , and we can take advantage of the lazy evaluation by returning an early false answer as soon as non-matching key/value pair is found. 地图具有内置的生成器.entries() ,一旦找到不匹配的键/值对,我们就可以通过返回早期的false答案来利用延迟评估。 That means we have to write our comparators in a slightly special way. 这意味着我们必须以一种稍微特殊的方式编写比较器。

const shortCircuitEqualComparator = (a, b, k) =>
  a === b ? true && k() : false

a and b are values of m1.get(somekey) and m2.get(somekey) respectively. ab数值 m1.get(somekey)m2.get(somekey)分别。 iff the two values are strictly equal ( === ), only then do we want to continue the comparison – in this case we return true && k() where k() is the remainder of the key/value pair comparison. 如果两个值严格相等( === ),则只有这样我们才要继续比较–在这种情况下,我们返回true && k() ,其中k()是键/值对比较的其余部分。 On the other hand, if a and b do not match, we can return an early false and skip comparing the rest of the values – ie, we already know that m1 and m2 do not match if any a / b pair do not match. 另一方面,如果ab不匹配,我们可以返回早期的false并跳过比较其余值–即,如果任何 aa / b不匹配,我们已经知道m1m2不匹配。

Finally, we can define mapEqual - it's simple too. 最后,我们可以定义mapEqual这也很简单。 It's just mapCompare using our special shortCircuitEqualComparator 这只是mapCompare用我们的特殊shortCircuitEqualComparator

const mapEqual = mapCompare (shortCircuitEqualComparator)

Let's take a quick look at how this works 让我们快速看一下它是如何工作的

// define two maps that are equal but have keys in different order
const a = new Map([['b', 2], ['a', 1]])
const b = new Map([['a', 1], ['b', 2]])

// define a third map that is not equal
const c = new Map([['a', 3], ['b', 2]])

// verify results
// a === b should be true
// b === a should be true
console.log('true', mapEqual(a, b)) // true true
console.log('true', mapEqual(b, a)) // true true

// a === c should be false
// c === a should be false too
console.log('false', mapEqual(a, c)) // false false
console.log('false', mapEqual(c, a)) // false false

Heck yes. 哎呀。 Things are looking good ... 事情看起来很好...


Deep comparisons with Rick & Morty 与Rick&Morty的深入比较

Now that we have a fricken sweet mapCompare generic, deep comparison is a breeze. 现在,我们有了一个贴切的贴图mapCompare泛型,进行深度比较变得轻而易举。 Take notice that we're actually implementing mapDeepCompare using mapCompare itself. 请注意,我们实际上是mapDeepCompare使用mapCompare本身实现mapDeepCompare

We use a custom comparator that simply checks if a and b are both Map objects – if so, we recurse using mapDeepCompare on the nested Maps; 我们使用了一个自定义比较器,该比较器仅检查ab是否均为Map对象-如果是,则使用嵌套Maps上的mapDeepCompare进行递归; also being mindful to call ... && k() to ensure the remaining key/value pairs are compared. 还应注意调用... && k()以确保比较其余键/值对。 If, a or b is a non-Map object, normal comparison is doing using f and we pass the continuation k along directly 如果ab是非Map对象,则正常比较是使用f ,我们直接将延续k传递给

const mapDeepCompare = f => mapCompare ((a, b, k) => {
  console.log(a, b)
  if (a instanceof Map && b instanceof Map)
    return mapDeepCompare (f) (a,b) ? true && k() : false
  else
    return f(a,b,k)
})

Now with mapDeepCompare , we can perform any type of deep comparison on nested Maps. 现在,有了mapDeepCompare ,我们可以对嵌套地图执行任何类型的深度比较。 Remember, equality is just one of the things we can be checking. 记住, 平等只是我们可以检查的事情之一。

Without further ado, mapDeepEqual . mapDeepEqualmapDeepEqual Of importance, we get to reuse our shortCircuitEqualComparator that we defined before. 重要的是,我们必须重用我们之前定义的shortCircuitEqualComparator This very clearly demonstrates that our comparators can be (re)used for shallow or deep Map comparisons. 这非常清楚地表明,我们的比较器可以(重新)用于浅深Map比较。

const mapDeepEqual = mapDeepCompare (shortCircuitEqualComparator)  

Let's see it work 让我们看看它的工作原理

// define two nested maps that are equal but have different key order
const e = new Map([
  ['a', 1],
  ['b', new Map([['c', 2]])]
])

const f = new Map([
  ['b', new Map([['c', 2]])],
  ['a', 1]
])

// define a third nested map that is not equal
const g = new Map([
  ['b', new Map([
    ['c', 3]  
  ])],
  ['a', 1]
])

// e === f should be true
// f === e should also be true
console.log('true', mapDeepEqual(e, f)) // true
console.log('true', mapDeepEqual(f, e)) // true

// e === g should be false
// g === e should also be false
console.log('false', mapDeepEqual(e, g)) // false
console.log('false', mapDeepEqual(g, e)) // false

OK, and just to make sure. 好的,只是为了确保。 What happens if we call mapEqual on our nested Maps e and f ? 如果我们在嵌套Maps ef上调用mapEqual会怎样? Since mapEqual does shallow comparison, we expect that the result should be false 由于mapEqual 浅层比较,因此我们期望结果应该为false

console.log('false', mapEqual(e, f)) // false
console.log('false', mapEqual(f, e)) // false

And there you have it. 那里有它。 Shallow and deep comparison of ES6 Map objects. 浅层和深层比较ES6 Map对象。 A nearly identical set of functions could be written to support ES6 Set. 可以编写几乎相同的一组功能来支持ES6集。 I'll leave this as an exercise for the readers. 我将其留给读者练习。


Runnable code demo 可运行的代码演示

This is all of the code above compiled into a single runnable demo. 这是上面所有被编译为单个可运行演示的代码。 Each console.log call outputs <expected>, <actual> . 每个console.log调用输出<expected>, <actual> So true, true or false, false would be a passing test – whereas true, false would be a failed test. 因此, true, truefalse, false将是通过测试–而true, false将是失败的测试。

 // mapCompare :: ((a, a, (* -> Bool)) -> Bool) -> (Map(k:a), Map(k:a)) -> Bool const mapCompare = f => (m1, m2) => { const aux = (it, m2) => { let {value, done} = it.next() if (done) return true let [k, v] = value return f (v, m2.get(k), $=> aux(it, m2)) } return aux(m1.entries(), m2) && aux(m2.entries(), m1) } // mapDeepCompare :: ((a, a, (* -> Bool)) -> Bool) -> (Map(k:a), Map(k:a)) -> Bool const mapDeepCompare = f => mapCompare ((a, b, k) => { if (a instanceof Map && b instanceof Map) return mapDeepCompare (f) (a,b) ? true && k() : false else return f(a,b,k) }) // shortCircuitEqualComparator :: (a, a, (* -> Bool)) -> Bool const shortCircuitEqualComparator = (a, b, k) => a === b ? true && k() : false // mapEqual :: (Map(k:a), Map(k:a)) -> Bool const mapEqual = mapCompare (shortCircuitEqualComparator) // mapDeepEqual :: (Map(k:a), Map(k:a)) -> Bool const mapDeepEqual = mapDeepCompare (shortCircuitEqualComparator) // fixtures const a = new Map([['b', 2], ['a', 1]]) const b = new Map([['a', 1], ['b', 2]]) const c = new Map([['a', 3], ['b', 2]]) const d = new Map([['a', 1], ['c', 2]]) const e = new Map([['a', 1], ['b', new Map([['c', 2]])]]) const f = new Map([['b', new Map([['c', 2]])], ['a', 1]]) const g = new Map([['b', new Map([['c', 3]])], ['a', 1]]) // shallow comparison of two equal maps console.log('true', mapEqual(a, b)) console.log('true', mapEqual(b, a)) // shallow comparison of two non-equal maps (differing values) console.log('false', mapEqual(a, c)) console.log('false', mapEqual(c, a)) // shallow comparison of two other non-equal maps (differing keys) console.log('false', mapEqual(a, d)) console.log('false', mapEqual(d, a)) // deep comparison of two equal nested maps console.log('true', mapDeepEqual(e, f)) console.log('true', mapDeepEqual(f, e)) // deep comparison of two non-equal nested maps console.log('false', mapDeepEqual(e, g)) console.log('false', mapDeepEqual(g, e)) // shallow comparison of two equal nested maps console.log('false', mapEqual(e, f)) console.log('false', mapEqual(f, e)) 

I just created a library ( @nodeguy/assert ) to solve this: 我刚刚创建了一个库( @ nodeguy / assert )解决此问题:

const assert = require('@nodeguy/assert')

// ES6 Map
var m1 = new Map();
m1.set('one', 1);
var m2 = new Map();
m2.set('two', 2);
assert.deepEqual(m1,m2);     // outputs: failed.

// ES6 Set
var s1 = new Set();
s1.add(1);
var s2 = new Set();
s2.add(2);
assert.deepEqual(s1,s2);     // outputs: failed.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM