Repository: chaijs/chai (js)

Issue:
Handling of duplicates when comparing sets
Current behaviour is that 
```
[3,1,2,3].should.have.deep.members([3,2,1]);      
[1,2,3].should.have.deep.members([3,2,1,3]);
[1,2,3].should.deep.include.members([3,2,1,3]);
chai.assert.sameDeepMembers([1,2,3],[3,2,1,3]);
```
all pass.
The documentation uses language of "sets" not "multisets" so it is hard to judge what should be the good behaviour when the arguments contain duplicates. 
One could argue that current implementation of `should.deep.include.members` is consistent with the documentation, as indeed every element of `[3,2,1,3]` belongs to `[1,2,3]`.
Similarly, one could argue similarly for `should.have.deep.members`.
But in practice I found myself in situations where what I really want to test is if the `actual` is a permutation of `expected`. There are many situations in which an order in the array cannot be guaranteed (say, `Object.keys(obj)`).

The issue might seem not so controversial when the arguments are written as literals and one can easily see what is the problem.
But in a more realistic scenarios like:
```
emittedEvents.should.have.deep.members(expectedEvents)
```
one could easily be fooled into thinking that this assertion simply means "expectedEvents is a permutation of expectedEvents" only to be surprised that firing the same event twice does not violate this test. 
Similarly one could have something like:
```
sorted.should.have.deep.members(inputArray)
```
and never spot an error in the implementation which caused removal of duplicates.

Perhaps it would be a good thing to have a separate `should.be.permutation.of` assertion for that case. But care must be taken to redact documentation for the other assertions so they do not suggest wrongly that they could be used for that.

Pull request description:
Fix duplicate handling in members assertion
- Changes `same.members` to properly handle duplicates by treating each one as a unique member
- Fixes #590 

Prior to this PR, the behavior was for duplicates to be ignored when performing any kind of members assertion. This led to unexpected behavior when asserting for sameness, as exemplified in #590. We consider this to be a bug.

With this PR, duplicates are no longer ignored when asserting for sameness, but are still ignored when asserting for inclusion. Bug fixed!

An argument could be made that duplicates should never be ignored, even for inclusion assertions. But  that's a breaking change instead of a bug fix. Therefore, that debate will be left for another day. It'd be easy to make that change in the future if agreed upon.

Patch:
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index 388ade2..32f3907 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -1571,15 +1571,33 @@ module.exports = function (chai, _) {
   Assertion.addMethod('closeTo', closeTo);
   Assertion.addMethod('approximately', closeTo);
 
-  function isSubsetOf(subset, superset, cmp, ordered) {
+  // Note: Duplicates are ignored if testing for inclusion instead of sameness.
+  function isSubsetOf(subset, superset, cmp, contains, ordered) {
+    if (!contains) {
+      if (subset.length !== superset.length) return false;
+      superset = superset.slice();
+    }
+
     return subset.every(function(elem, idx) {
       if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx];
-      if (!cmp) return superset.indexOf(elem) !== -1;
 
-      return superset.some(function(elem2) {
-        return cmp(elem, elem2);
+      if (!cmp) {
+        var matchIdx = superset.indexOf(elem);
+        if (matchIdx === -1) return false;
+       
+        // Remove match from superset so not counted twice if duplicate in subset.
+        if (!contains) superset.splice(matchIdx, 1);
+        return true;
+      }
+
+      return superset.some(function(elem2, matchIdx) {
+        if (!cmp(elem, elem2)) return false;
+
+        // Remove match from superset so not counted twice if duplicate in subset.
+        if (!contains) superset.splice(matchIdx, 1);
+        return true;
       });
-    })
+    });
   }
 
   /**
@@ -1594,7 +1612,7 @@ module.exports = function (chai, _) {
    *     expect([1, 2, 3]).to.not.have.members([5, 1, 3]);
    *
    * If the `contains` flag is set via `.include` or `.contain`, instead asserts
-   * that the target is a superset of `set`.
+   * that the target is a superset of `set`. Duplicates are ignored.
    *
    *     expect([1, 2, 3]).to.include.members([2, 1]);
    *     expect([1, 2, 3]).to.not.include.members([5, 1]);
@@ -1637,19 +1655,16 @@ module.exports = function (chai, _) {
     new Assertion(obj).to.be.an('array');
     new Assertion(subset).to.be.an('array');
 
+    var contains = flag(this, 'contains');
     var ordered = flag(this, 'ordered');
 
     var failMsg, failNegateMsg, lengthCheck;
 
-    if (flag(this, 'contains')) {
-      lengthCheck = true;
-
+    if (contains) {
       var subject = ordered ? 'an ordered superset' : 'a superset';
       failMsg = 'expected #{this} to be ' + subject + ' of #{exp}';
       failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}';
     } else {
-      lengthCheck = obj.length === subset.length;
-
       var subject = ordered ? 'ordered members' : 'members';
       failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}';
       failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}';
@@ -1658,7 +1673,7 @@ module.exports = function (chai, _) {
     var cmp = flag(this, 'deep') ? _.eql : undefined;
 
     this.assert(
-        lengthCheck && isSubsetOf(subset, obj, cmp, ordered)
+        isSubsetOf(subset, obj, cmp, contains, ordered)
       , failMsg
       , failNegateMsg
       , subset


Test patch:
diff --git a/test/assert.js b/test/assert.js
index bf5d763..b91f808 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -1173,6 +1173,7 @@ describe('assert', function () {
     assert.sameMembers([], []);
     assert.sameMembers([1, 2, 3], [3, 2, 1]);
     assert.sameMembers([4, 2], [4, 2]);
+    assert.sameMembers([4, 2, 2], [4, 2, 2]);
 
     err(function() {
       assert.sameMembers([], [1, 2]);
@@ -1185,6 +1186,11 @@ describe('assert', function () {
 
   it('notSameMembers', function() {
     assert.notSameMembers([1, 2, 3], [2, 1, 5]);
+    assert.notSameMembers([1, 2, 3], [1, 2, 3, 3]);
+    assert.notSameMembers([1, 2], [1, 2, 2]);
+    assert.notSameMembers([1, 2, 2], [1, 2]);
+    assert.notSameMembers([1, 2, 2], [1, 2, 3]);
+    assert.notSameMembers([1, 2, 3], [1, 2, 2]);
     assert.notSameMembers([{a: 1}], [{a: 1}]);
 
     err(function() {
@@ -1195,6 +1201,7 @@ describe('assert', function () {
   it('sameDeepMembers', function() {
     assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members');
     assert.sameDeepMembers([ {b: 3}, {a: 2}, 5, "hello" ], [ "hello", 5, {b: 3}, {a: 2} ], 'same deep members');
+    assert.sameDeepMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       assert.sameDeepMembers([ {b: 3} ], [ {c: 3} ])
@@ -1207,6 +1214,10 @@ describe('assert', function () {
 
   it('notSameDeepMembers', function() {
     assert.notSameDeepMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {f: 5}]);
+    assert.notSameDeepMembers([{a: 1}, {b: 2}], [{a: 1}, {b: 2}, {b: 2}]);
+    assert.notSameDeepMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}]);
+    assert.notSameDeepMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}, {c: 3}]);
+    assert.notSameDeepMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       assert.notSameDeepMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
@@ -1215,6 +1226,7 @@ describe('assert', function () {
 
   it('sameOrderedMembers', function() {
     assert.sameOrderedMembers([1, 2, 3], [1, 2, 3]);
+    assert.sameOrderedMembers([1, 2, 2], [1, 2, 2]);
 
     err(function() {
       assert.sameOrderedMembers([1, 2, 3], [2, 1, 3]);
@@ -1224,6 +1236,10 @@ describe('assert', function () {
   it('notSameOrderedMembers', function() {
     assert.notSameOrderedMembers([1, 2, 3], [2, 1, 3]);
     assert.notSameOrderedMembers([1, 2, 3], [1, 2]);
+    assert.notSameOrderedMembers([1, 2], [1, 2, 2]);
+    assert.notSameOrderedMembers([1, 2, 2], [1, 2]);
+    assert.notSameOrderedMembers([1, 2, 2], [1, 2, 3]);
+    assert.notSameOrderedMembers([1, 2, 3], [1, 2, 2]);
 
     err(function() {
       assert.notSameOrderedMembers([1, 2, 3], [1, 2, 3]);
@@ -1232,6 +1248,7 @@ describe('assert', function () {
 
   it('sameDeepOrderedMembers', function() {
     assert.sameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {c: 3}]);
+    assert.sameDeepOrderedMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       assert.sameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
@@ -1241,6 +1258,10 @@ describe('assert', function () {
   it('notSameDeepOrderedMembers', function() {
     assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
     assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {f: 5}]);
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}], [{a: 1}, {b: 2}, {b: 2}]);
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}]);
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {b: 2}], [{a: 1}, {b: 2}, {c: 3}]);
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {c: 3}]);
@@ -1305,6 +1326,7 @@ describe('assert', function () {
   it('notIncludeOrderedMembers', function() {
     assert.notIncludeOrderedMembers([1, 2, 3], [2, 1]);
     assert.notIncludeOrderedMembers([1, 2, 3], [2, 3]);
+    assert.notIncludeOrderedMembers([1, 2, 3], [1, 2, 2]);
 
     err(function() {
       assert.notIncludeOrderedMembers([1, 2, 3], [1, 2]);
@@ -1322,6 +1344,7 @@ describe('assert', function () {
   it('notIncludeDeepOrderedMembers', function() {
     assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}]);
     assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {f: 5}]);
+    assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}]);
diff --git a/test/expect.js b/test/expect.js
index 5706b41..0fa2d91 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -1367,6 +1367,7 @@ describe('expect', function () {
   it('include.members', function() {
     expect([1, 2, 3]).to.include.members([]);
     expect([1, 2, 3]).to.include.members([3, 2]);
+    expect([1, 2, 3]).to.include.members([3, 2, 2]);
     expect([1, 2, 3]).to.not.include.members([8, 4]);
     expect([1, 2, 3]).to.not.include.members([1, 2, 3, 4]);
     expect([{a: 1}]).to.not.include.members([{a: 1}]);
@@ -1383,17 +1384,27 @@ describe('expect', function () {
   it('same.members', function() {
     expect([5, 4]).to.have.same.members([4, 5]);
     expect([5, 4]).to.have.same.members([5, 4]);
+    expect([5, 4, 4]).to.have.same.members([5, 4, 4]);
     expect([5, 4]).to.not.have.same.members([]);
     expect([5, 4]).to.not.have.same.members([6, 3]);
     expect([5, 4]).to.not.have.same.members([5, 4, 2]);
+    expect([5, 4]).to.not.have.same.members([5, 4, 4]);
+    expect([5, 4, 4]).to.not.have.same.members([5, 4]);
+    expect([5, 4, 4]).to.not.have.same.members([5, 4, 3]);
+    expect([5, 4, 3]).to.not.have.same.members([5, 4, 4]);
   });
 
   it('members', function() {
     expect([5, 4]).members([4, 5]);
     expect([5, 4]).members([5, 4]);
+    expect([5, 4, 4]).members([5, 4, 4]);
     expect([5, 4]).not.members([]);
     expect([5, 4]).not.members([6, 3]);
     expect([5, 4]).not.members([5, 4, 2]);
+    expect([5, 4]).not.members([5, 4, 4]);
+    expect([5, 4, 4]).not.members([5, 4]);
+    expect([5, 4, 4]).not.members([5, 4, 3]);
+    expect([5, 4, 3]).not.members([5, 4, 4]);
     expect([{ id: 1 }]).not.members([{ id: 1 }]);
 
     err(function() {
@@ -1407,16 +1418,39 @@ describe('expect', function () {
 
   it('deep.members', function() {
     expect([{ id: 1 }]).deep.members([{ id: 1 }]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).deep.members([{a: 1}, {b: 2}, {b: 2}]);
+
     expect([{ id: 2 }]).not.deep.members([{ id: 1 }]);
+    expect([{a: 1}, {b: 2}]).not.deep.members([{a: 1}, {b: 2}, {b: 2}]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).not.deep.members([{a: 1}, {b: 2}]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).not.deep.members([{a: 1}, {b: 2}, {c: 3}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.deep.members([{a: 1}, {b: 2}, {b: 2}]);
+
     err(function(){
       expect([{ id: 1 }]).deep.members([{ id: 2 }])
     }, 'expected [ { id: 1 } ] to have the same members as [ { id: 2 } ]');
   });
 
+  it('include.deep.members', function() {
+    expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.members([{b: 2}, {a: 1}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.members([{b: 2}, {a: 1}, {a: 1}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.members([{b: 2}, {a: 1}, {f: 5}]);
+
+    err(function() {
+      expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.members([{b: 2}, {a: 1}, {f: 5}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to be a superset of [ { b: 2 }, { a: 1 }, { f: 5 } ]');
+  });
+
   it('ordered.members', function() {
     expect([1, 2, 3]).ordered.members([1, 2, 3]);
+    expect([1, 2, 2]).ordered.members([1, 2, 2]);
+
     expect([1, 2, 3]).not.ordered.members([2, 1, 3]);
     expect([1, 2, 3]).not.ordered.members([1, 2]);
+    expect([1, 2]).not.ordered.members([1, 2, 2]);
+    expect([1, 2, 2]).not.ordered.members([1, 2]);
+    expect([1, 2, 2]).not.ordered.members([1, 2, 3]);
+    expect([1, 2, 3]).not.ordered.members([1, 2, 2]);
 
     err(function() {
       expect([1, 2, 3]).ordered.members([2, 1, 3]);
@@ -1431,6 +1465,7 @@ describe('expect', function () {
     expect([1, 2, 3]).include.ordered.members([1, 2]);
     expect([1, 2, 3]).not.include.ordered.members([2, 1]);
     expect([1, 2, 3]).not.include.ordered.members([2, 3]);
+    expect([1, 2, 3]).not.include.ordered.members([1, 2, 2]);
 
     err(function() {
       expect([1, 2, 3]).include.ordered.members([2, 1]);
@@ -1443,7 +1478,13 @@ describe('expect', function () {
 
   it('deep.ordered.members', function() {
     expect([{a: 1}, {b: 2}, {c: 3}]).deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
+
     expect([{a: 1}, {b: 2}, {c: 3}]).not.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+    expect([{a: 1}, {b: 2}]).not.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).not.deep.ordered.members([{a: 1}, {b: 2}]);
+    expect([{a: 1}, {b: 2}, {b: 2}]).not.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       expect([{a: 1}, {b: 2}, {c: 3}]).deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
@@ -1458,6 +1499,7 @@ describe('expect', function () {
     expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.ordered.members([{a: 1}, {b: 2}]);
     expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{b: 2}, {a: 1}]);
     expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{b: 2}, {c: 3}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.ordered.members([{b: 2}, {a: 1}]);
diff --git a/test/should.js b/test/should.js
index 0aec0d6..bfe19ff 100644
--- a/test/should.js
+++ b/test/should.js
@@ -1204,6 +1204,7 @@ describe('should', function() {
     [1, 2, 3].should.include.members([3]);
     [1, 2, 3].should.include.members([]);
     [1, 2, 3].should.include.members([2, 1]);
+    [1, 2, 3].should.include.members([2, 1, 1]);
 
     [1, 2, 3].should.not.include.members([999]);
     [].should.not.include.members([23]);
@@ -1229,8 +1230,13 @@ describe('should', function() {
   it('memberEquals', function() {
     [1, 2, 3].should.have.same.members([3, 2, 1]);
     [5, 4].should.have.same.members([5, 4]);
+    [5, 4, 4].should.have.same.members([5, 4, 4]);
     [].should.have.same.members([]);
 
+    [5, 4].should.not.have.same.members([5, 4, 4]);
+    [5, 4, 4].should.not.have.same.members([5, 4]);
+    [5, 4, 4].should.not.have.same.members([5, 4, 3]);
+    [5, 4, 3].should.not.have.same.members([5, 4, 4]);
     [{a: 1}].should.not.have.same.members([{a: 1}]);
 
     err(function() {
@@ -1242,10 +1248,41 @@ describe('should', function() {
     }, 'expected 4 to be an array');
   });
 
+  it('deep.members', function() {
+    [{ id: 1 }].should.have.deep.members([{ id: 1 }]);
+    [{a: 1}, {b: 2}, {b: 2}].should.have.deep.members([{a: 1}, {b: 2}, {b: 2}]);
+
+    [{ id: 2 }].should.not.have.deep.members([{ id: 1 }]);
+    [{a: 1}, {b: 2}].should.not.have.deep.members([{a: 1}, {b: 2}, {b: 2}]);
+    [{a: 1}, {b: 2}, {b: 2}].should.not.have.deep.members([{a: 1}, {b: 2}]);
+    [{a: 1}, {b: 2}, {b: 2}].should.not.have.deep.members([{a: 1}, {b: 2}, {c: 3}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.have.deep.members([{a: 1}, {b: 2}, {b: 2}]);
+
+    err(function(){
+      [{ id: 1 }].should.have.deep.members([{ id: 2 }])
+    }, 'expected [ { id: 1 } ] to have the same members as [ { id: 2 } ]');
+  });
+
+  it('include.deep.members', function() {
+    [{a: 1}, {b: 2}, {c: 3}].should.include.deep.members([{b: 2}, {a: 1}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.include.deep.members([{b: 2}, {a: 1}, {a: 1}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.members([{b: 2}, {a: 1}, {f: 5}]);
+
+    err(function() {
+      [{a: 1}, {b: 2}, {c: 3}].should.include.deep.members([{b: 2}, {a: 1}, {f: 5}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to be a superset of [ { b: 2 }, { a: 1 }, { f: 5 } ]');
+  });
+
   it('ordered.members', function() {
     [1, 2, 3].should.ordered.members([1, 2, 3]);
+    [1, 2, 2].should.ordered.members([1, 2, 2]);
+
     [1, 2, 3].should.not.ordered.members([2, 1, 3]);
     [1, 2, 3].should.not.ordered.members([1, 2]);
+    [1, 2].should.not.ordered.members([1, 2, 2]);
+    [1, 2, 2].should.not.ordered.members([1, 2]);
+    [1, 2, 2].should.not.ordered.members([1, 2, 3]);
+    [1, 2, 3].should.not.ordered.members([1, 2, 2]);
 
     err(function() {
       [1, 2, 3].should.ordered.members([2, 1, 3]);
@@ -1260,6 +1297,7 @@ describe('should', function() {
     [1, 2, 3].should.include.ordered.members([1, 2]);
     [1, 2, 3].should.not.include.ordered.members([2, 1]);
     [1, 2, 3].should.not.include.ordered.members([2, 3]);
+    [1, 2, 3].should.not.include.ordered.members([1, 2, 2]);
 
     err(function() {
       [1, 2, 3].should.include.ordered.members([2, 1]);
@@ -1272,7 +1310,13 @@ describe('should', function() {
 
   it('deep.ordered.members', function() {
     [{a: 1}, {b: 2}, {c: 3}].should.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    [{a: 1}, {b: 2}, {b: 2}].should.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
+
     [{a: 1}, {b: 2}, {c: 3}].should.not.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+    [{a: 1}, {b: 2}].should.not.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
+    [{a: 1}, {b: 2}, {b: 2}].should.not.deep.ordered.members([{a: 1}, {b: 2}]);
+    [{a: 1}, {b: 2}, {b: 2}].should.not.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       [{a: 1}, {b: 2}, {c: 3}].should.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
@@ -1287,6 +1331,7 @@ describe('should', function() {
     [{a: 1}, {b: 2}, {c: 3}].should.include.deep.ordered.members([{a: 1}, {b: 2}]);
     [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{b: 2}, {a: 1}]);
     [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{b: 2}, {c: 3}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{a: 1}, {b: 2}, {b: 2}]);
 
     err(function() {
       [{a: 1}, {b: 2}, {c: 3}].should.include.deep.ordered.members([{b: 2}, {a: 1}]);