Repository: chaijs/chai
Pull request #796 (2016-09-10 20:13:02)

Verify types on increase | decrease
Hello there again!

I saw that @meeber did a very good job and now chai 4.x.x is finally merged at master.

So here am I, aiming to solve the rest of #691.
This PR aims to check that the property being tested by increase | decrease is a number.
It also adds tests to `assert.atLeast` and `assert.atMost` since after the merge they automatically started to check types.

I don't know if I'm happy with the documentation changes, so please give your opinions on that.

Thank You


Patch:
diff --git a/.travis.yml b/.travis.yml
index e99d17c..12e86d2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,12 +3,7 @@ language: node_js
 addons:
   sauce_connect: true
 node_js:
- - 0.10 # to be removed 2016-10-01
- - 0.12 # to be removed 2016-12-31
- - 4 # to be removed 2018-04-01
- - 6 # to be removed 2019-04-01
- - lts/* # safety net; don't remove
- - node # safety net; don't remove
+- 0.1
 
 script:
 - make test-travisci
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index ab6a9f6..12f1674 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -224,7 +224,7 @@ module.exports = function (chai, _) {
    *     expect([1,2,3]).to.include(2);
    *     expect('foobar').to.contain('foo');
    *     expect({ foo: 'bar', hello: 'universe' }).to.include({ foo: 'bar' });
-   * 
+   *
    * By default, strict equality (===) is used. When asserting the inclusion of
    * a value in an array, the array is searched for an element that's strictly
    * equal to the given value. When asserting a subset of properties in an
@@ -319,7 +319,7 @@ module.exports = function (chai, _) {
 
       return;
     }
-    
+
     // Assert inclusion in an array or substring in a string.
     this.assert(
         typeof obj === 'string' || !isDeep ? ~obj.indexOf(val)
@@ -1764,7 +1764,7 @@ module.exports = function (chai, _) {
       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;
@@ -1959,19 +1959,26 @@ module.exports = function (chai, _) {
   Assertion.addChainableMethod('changes', assertChanges);
 
   /**
-   * ### .increase(function)
+   * ### .increase(target[, property[, message]])
    *
-   * Asserts that a function increases an object property
+   * Asserts that a function increases a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 15 };
    *     expect(fn).to.increase(obj, 'val');
    *
+   * It can also receive a function which returns the property
+   *
+   *     var obj = { val: 10 };
+   *     var fn = function() { obj.val = 5 };
+   *     var getVal = function() { return obj.val };
+   *     expect(fn).to.increase(getVal);
+   *
    * @name increase
    * @alias increases
    * @alias Increase
-   * @param {String} target
-   * @param {String} property name
+   * @param {String|Function} target
+   * @param {String} property name _optional_
    * @param {String} message _optional_
    * @namespace BDD
    * @api public
@@ -1991,6 +1998,9 @@ module.exports = function (chai, _) {
       initial = target[prop];
     }
 
+    // Make sure that the target is a number
+    new Assertion(initial).is.a('number');
+
     fn();
 
     var final = prop === undefined || prop === null ? target() : target[prop];
@@ -2013,19 +2023,26 @@ module.exports = function (chai, _) {
   Assertion.addChainableMethod('increases', assertIncreases);
 
   /**
-   * ### .decrease(function)
+   * ### .decrease(target[, property[, message]])
    *
-   * Asserts that a function decreases an object property
+   * Asserts that a function decreases a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 5 };
    *     expect(fn).to.decrease(obj, 'val');
    *
+   * It can also receive a function which returns the property
+   *
+   *     var obj = { val: 10 };
+   *     var fn = function() { obj.val = 5 };
+   *     var getVal = function() { return obj.val };
+   *     expect(fn).to.decrease(getVal);
+   *
    * @name decrease
    * @alias decreases
    * @alias Decrease
-   * @param {String} target
-   * @param {String} property name
+   * @param {String|Function} target
+   * @param {String} property name _optional_
    * @param {String} message _optional_
    * @namespace BDD
    * @api public
@@ -2045,6 +2062,9 @@ module.exports = function (chai, _) {
       initial = target[prop];
     }
 
+    // Make sure that the target is a number
+    new Assertion(initial).is.a('number');
+
     fn();
 
     var final = prop === undefined || prop === null ? target() : target[prop];
diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js
index 189fef4..9b09017 100644
--- a/lib/chai/interface/assert.js
+++ b/lib/chai/interface/assert.js
@@ -129,7 +129,6 @@ module.exports = function (chai, util) {
       , 'expected #{this} to not equal #{act}'
       , exp
       , act
-      , true
     );
   };
 
@@ -157,7 +156,6 @@ module.exports = function (chai, util) {
       , 'expected #{this} to equal #{act}'
       , exp
       , act
-      , true
     );
   };
 
@@ -2184,7 +2182,7 @@ module.exports = function (chai, util) {
   /**
    * ### .increases(function, object, property, [message])
    *
-   * Asserts that a function increases an object property
+   * Asserts that a function increases a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 13 };
@@ -2211,7 +2209,7 @@ module.exports = function (chai, util) {
   /**
    * ### .increasesBy(function, object, property, delta, [message])
    *
-   * Asserts that a function increases an object property or a function's return value by an amount (delta)
+   * Asserts that a function increases a numeric object property or a function's return value by an amount (delta)
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val += 10 };
@@ -2243,7 +2241,7 @@ module.exports = function (chai, util) {
   /**
    * ### .doesNotIncrease(function, object, property, [message])
    *
-   * Asserts that a function does not increase object property
+   * Asserts that a function does not increase a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 8 };
@@ -2270,7 +2268,7 @@ module.exports = function (chai, util) {
   /**
    * ### .increasesButNotBy(function, object, property, [message])
    *
-   * Asserts that a function does not increase object property or function's return value by an amount (delta)
+   * Asserts that a function does not increase a numeric object property or function's return value by an amount (delta)
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 15 };
@@ -2302,7 +2300,7 @@ module.exports = function (chai, util) {
   /**
    * ### .decreases(function, object, property, [message])
    *
-   * Asserts that a function decreases an object property
+   * Asserts that a function decreases a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 5 };
@@ -2329,7 +2327,7 @@ module.exports = function (chai, util) {
   /**
    * ### .decreasesBy(function, object, property, delta, [message])
    *
-   * Asserts that a function decreases an object property or a function's return value by an amount (delta)
+   * Asserts that a function decreases a numeric object property or a function's return value by an amount (delta)
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val -= 5 };
@@ -2361,7 +2359,7 @@ module.exports = function (chai, util) {
   /**
    * ### .doesNotDecrease(function, object, property, [message])
    *
-   * Asserts that a function does not decreases an object property
+   * Asserts that a function does not decreases a numeric object property
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 15 };
@@ -2388,7 +2386,7 @@ module.exports = function (chai, util) {
   /**
    * ### .doesNotDecreaseBy(function, object, property, delta, [message])
    *
-   * Asserts that a function does not decreases an object property or a function's return value by an amount (delta)
+   * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta)
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 5 };
@@ -2420,7 +2418,7 @@ module.exports = function (chai, util) {
   /**
    * ### .decreasesButNotBy(function, object, property, delta, [message])
    *
-   * Asserts that a function does not decreases an object property or a function's return value by an amount (delta)
+   * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta)
    *
    *     var obj = { val: 10 };
    *     var fn = function() { obj.val = 5 };
diff --git a/package.json b/package.json
index 6418666..34cdb1b 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
     "test": "make test"
   },
   "engines": {
-    "node": ">=0.10"
+    "node": ">= 0.4.0"
   },
   "dependencies": {
     "assertion-error": "^1.0.1",


Test patch:
diff --git a/test/assert.js b/test/assert.js
index 449a178..e8219c6 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -365,7 +365,7 @@ describe('assert', function () {
   it('exists', function() {
     var meeber = 'awesome';
     var iDoNotExist;
-       
+
     assert.exists(meeber);
     assert.exists(0);
     assert.exists(false);
@@ -379,7 +379,7 @@ describe('assert', function () {
   it('notExists', function() {
     var meeber = 'awesome';
     var iDoNotExist;
-       
+
     assert.notExists(iDoNotExist);
 
     err(function (){
@@ -728,7 +728,7 @@ describe('assert', function () {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testMap = new Map();
-      
+
       testMap.set(aKey, 'aValue');
       testMap.set(anotherKey, 'anotherValue');
 
@@ -751,7 +751,7 @@ describe('assert', function () {
         , weirdMapKey2 = {toString: NaN}
         , weirdMapKey3 = []
         , weirdMap = new Map();
-      
+
       weirdMap.set(weirdMapKey1, 'val1');
       weirdMap.set(weirdMapKey2, 'val2');
 
@@ -763,7 +763,7 @@ describe('assert', function () {
           , symMapKey2 = Symbol()
           , symMapKey3 = Symbol()
           , symMap = new Map();
-        
+
         symMap.set(symMapKey1, 'val1');
         symMap.set(symMapKey2, 'val2');
 
@@ -825,7 +825,7 @@ describe('assert', function () {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testSet = new Set();
-      
+
       testSet.add(aKey);
       testSet.add(anotherKey);
 
@@ -848,7 +848,7 @@ describe('assert', function () {
         , weirdSetKey2 = {toString: NaN}
         , weirdSetKey3 = []
         , weirdSet = new Set();
-      
+
       weirdSet.add(weirdSetKey1);
       weirdSet.add(weirdSetKey2);
 
@@ -860,7 +860,7 @@ describe('assert', function () {
           , symSetKey2 = Symbol()
           , symSetKey3 = Symbol()
           , symSet = new Set();
-        
+
         symSet.add(symSetKey1);
         symSet.add(symSetKey2);
 
@@ -873,7 +873,7 @@ describe('assert', function () {
       }
 
       var errSet = new Set();
-      
+
       errSet.add({1: 20});
       errSet.add('number');
 
@@ -1693,6 +1693,13 @@ describe('assert', function () {
     err(function() {
       assert.isAtLeast(1, 3);
     }, 'expected 1 to be at least 3');
+
+    err(function() {
+      assert.isAtLeast(null, 1);
+    }, 'expected null to be a number');
+    err(function() {
+      assert.isAtLeast(1, null);
+    }, 'the argument to least must be a number');
   });
 
   it('below', function() {
@@ -1722,6 +1729,13 @@ describe('assert', function () {
     err(function() {
       assert.isAtMost(3, 1);
     }, 'expected 3 to be at most 1');
+
+    err(function() {
+      assert.isAtMost(null, 1);
+    }, 'expected null to be a number');
+    err(function() {
+      assert.isAtMost(1, null);
+    }, 'the argument to most must be a number');
   });
 
   it('change', function() {
@@ -1749,7 +1763,7 @@ describe('assert', function () {
   });
 
   it('increase, decrease', function() {
-    var obj = { value: 10 },
+    var obj = { value: 10, noop: null },
         arr = ['one', 'two'],
         pFn   = function() { arr.push('three') },
         popFn = function() { arr.pop() },
@@ -1777,6 +1791,13 @@ describe('assert', function () {
     assert.doesNotIncrease(popFn, lenFn);
     assert.increasesBy(pFn, lenFn, 1);
     assert.increasesButNotBy(pFn, lenFn, 2);
+
+    err(function() {
+      assert.increases(incFn, obj, 'noop');
+    }, 'expected null to be a number');
+    err(function() {
+      assert.decreases(incFn, obj, 'noop');
+    }, 'expected null to be a number');
   });
 
   it('isExtensible / extensible', function() {
diff --git a/test/expect.js b/test/expect.js
index be4d92f..f6eaedd 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -603,7 +603,7 @@ describe('expect', function () {
     err(function(){
       expect().to.not.be.empty;
     }, "expected undefined to exist");
-    
+
   });
 
   it('NaN', function() {
@@ -797,7 +797,7 @@ describe('expect', function () {
       expect(obj).to.not.have.deep.property('a', {b: 1}, 'blah');
     }, "blah: expected { a: { b: 1 } } to not have a deep property 'a' of { b: 1 }");
   });
-  
+
   it('nested.property(name, val)', function(){
     expect({ foo: { bar: 'baz' } })
       .to.have.nested.property('foo.bar', 'baz');
@@ -1182,7 +1182,7 @@ describe('expect', function () {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testMap = new Map();
-      
+
       testMap.set(aKey, 'aValue');
       testMap.set(anotherKey, 'anotherValue');
 
@@ -1209,7 +1209,7 @@ describe('expect', function () {
         , weirdMapKey2 = {toString: NaN}
         , weirdMapKey3 = []
         , weirdMap = new Map();
-      
+
       weirdMap.set(weirdMapKey1, 'val1');
       weirdMap.set(weirdMapKey2, 'val2');
 
@@ -1221,7 +1221,7 @@ describe('expect', function () {
           , symMapKey2 = Symbol()
           , symMapKey3 = Symbol()
           , symMap = new Map();
-        
+
         symMap.set(symMapKey1, 'val1');
         symMap.set(symMapKey2, 'val2');
 
@@ -1262,7 +1262,7 @@ describe('expect', function () {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testSet = new Set();
-      
+
       testSet.add(aKey);
       testSet.add(anotherKey);
 
@@ -1289,7 +1289,7 @@ describe('expect', function () {
         , weirdSetKey2 = {toString: NaN}
         , weirdSetKey3 = []
         , weirdSet = new Set();
-      
+
       weirdSet.add(weirdSetKey1);
       weirdSet.add(weirdSetKey2);
 
@@ -1301,7 +1301,7 @@ describe('expect', function () {
           , symSetKey2 = Symbol()
           , symSetKey3 = Symbol()
           , symSet = new Set();
-        
+
         symSet.add(symSetKey1);
         symSet.add(symSetKey2);
 
@@ -1546,87 +1546,87 @@ describe('expect', function () {
 
     err(function(){
       expect(goodFn).to.throw();
-    }, /^expected \[Function(: goodFn)*\] to throw an error$/);
+    }, "expected [Function] to throw an error");
 
     err(function(){
       expect(goodFn).to.throw(ReferenceError);
-    }, /^expected \[Function(: goodFn)*\] to throw ReferenceError$/);
+    }, "expected [Function] to throw ReferenceError");
 
     err(function(){
       expect(goodFn).to.throw(specificError);
-    }, /^expected \[Function(: goodFn)*\] to throw 'RangeError: boo'$/);
+    }, "expected [Function] to throw 'RangeError: boo'");
 
     err(function(){
       expect(badFn).to.not.throw();
-    }, /^expected \[Function(: badFn)*\] to not throw an error but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw an error but 'Error: testing' was thrown");
 
     err(function(){
       expect(badFn).to.throw(ReferenceError);
-    }, /^expected \[Function(: badFn)*\] to throw 'ReferenceError' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'ReferenceError' but 'Error: testing' was thrown");
 
     err(function(){
       expect(badFn).to.throw(specificError);
-    }, /^expected \[Function(: badFn)*\] to throw 'RangeError: boo' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'RangeError: boo' but 'Error: testing' was thrown");
 
     err(function(){
       expect(badFn).to.not.throw(Error);
-    }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown");
 
     err(function(){
       expect(refErrFn).to.not.throw(ReferenceError);
-    }, /^expected \[Function(: refErrFn)*\] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown$/);
+    }, "expected [Function] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown");
 
     err(function(){
       expect(badFn).to.throw(PoorlyConstructedError);
-    }, /^expected \[Function(: badFn)*\] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown");
 
     err(function(){
       expect(ickyErrFn).to.not.throw(PoorlyConstructedError);
-    }, /^(expected \[Function(: ickyErrFn)*\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
+    }, /^(expected \[Function\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
 
     err(function(){
       expect(ickyErrFn).to.throw(ReferenceError);
-    }, /^(expected \[Function(: ickyErrFn)*\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
+    }, /^(expected \[Function\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
 
     err(function(){
       expect(specificErrFn).to.throw(new ReferenceError('eek'));
-    }, /^expected \[Function(: specificErrFn)*\] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown$/);
+    }, "expected [Function] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown");
 
     err(function(){
       expect(specificErrFn).to.not.throw(specificError);
-    }, /^expected \[Function(: specificErrFn)*\] to not throw 'RangeError: boo'$/);
+    }, "expected [Function] to not throw 'RangeError: boo'");
 
     err(function (){
       expect(badFn).to.not.throw(/testing/);
-    }, /^expected \[Function(: badFn)*\] to throw error not matching \/testing\/$/);
+    }, "expected [Function] to throw error not matching /testing/");
 
     err(function () {
       expect(badFn).to.throw(/hello/);
-    }, /^expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/);
+    }, "expected [Function] to throw error matching /hello/ but got 'testing'");
 
     err(function () {
       expect(badFn).to.throw(Error, /hello/, 'blah');
-    }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/);
+    }, "blah: expected [Function] to throw error matching /hello/ but got 'testing'");
 
     err(function () {
       expect(badFn).to.throw(Error, 'hello', 'blah');
-    }, /^blah: expected \[Function(: badFn)*\] to throw error including 'hello' but got 'testing'$/);
+    }, "blah: expected [Function] to throw error including 'hello' but got 'testing'");
 
     err(function () {
       (customErrFn).should.not.throw();
-    }, /^expected \[Function(: customErrFn)*\] to not throw an error but 'CustomError: foo' was thrown$/);
+    }, "expected [Function] to not throw an error but 'CustomError: foo' was thrown");
 
     err(function(){
       expect(badFn).to.not.throw(Error, 'testing');
-    }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown");
 
     err(function(){
       expect(emptyStringErrFn).to.not.throw(Error, '');
-    }, /^expected \[Function(: emptyStringErrFn)*\] to not throw 'Error' but 'Error' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error' was thrown");
 
     err(function(){
       expect(emptyStringErrFn).to.not.throw('');
-    }, /^expected \[Function(: emptyStringErrFn)*\] to throw error not including ''$/);
+    }, "expected [Function] to throw error not including ''");
   });
 
   it('respondTo', function(){
@@ -1662,7 +1662,7 @@ describe('expect', function () {
 
     err(function(){
       expect(2).to.satisfy(matcher, 'blah');
-    }, /^blah: expected 2 to satisfy \[Function(: matcher)*\]$/);
+    }, "blah: expected 2 to satisfy [Function]");
   });
 
   it('closeTo', function(){
@@ -1898,7 +1898,7 @@ describe('expect', function () {
   });
 
   it('increase, decrease', function() {
-    var obj = { value: 10 },
+    var obj = { value: 10, noop: null },
         arr = ['one', 'two'],
         pFn   = function() { arr.push('three') },
         popFn = function() { arr.pop() },
@@ -1931,6 +1931,13 @@ describe('expect', function () {
     expect(popFn).to.decrease(lenFn).but.not.by(2);
     expect(nFn).to.not.decrease(lenFn);
     expect(pFn).to.not.decrease(lenFn);
+
+    err(function() {
+      expect(incFn).to.increase(obj, 'noop');
+    }, 'expected null to be a number');
+    err(function() {
+      expect(incFn).to.decrease(obj, 'noop');
+    }, 'expected null to be a number');
   });
 
   it('extensible', function() {
diff --git a/test/should.js b/test/should.js
index 5282bac..e89055c 100644
--- a/test/should.js
+++ b/test/should.js
@@ -1030,7 +1030,7 @@ describe('should', function() {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testMap = new Map();
-      
+
       testMap.set(aKey, 'aValue');
       testMap.set(anotherKey, 'anotherValue');
 
@@ -1055,7 +1055,7 @@ describe('should', function() {
         , weirdMapKey2 = {toString: NaN}
         , weirdMapKey3 = []
         , weirdMap = new Map();
-      
+
       weirdMap.set(weirdMapKey1, 'val1');
       weirdMap.set(weirdMapKey2, 'val2');
 
@@ -1067,7 +1067,7 @@ describe('should', function() {
           , symMapKey2 = Symbol()
           , symMapKey3 = Symbol()
           , symMap = new Map();
-        
+
         symMap.set(symMapKey1, 'val1');
         symMap.set(symMapKey2, 'val2');
 
@@ -1108,7 +1108,7 @@ describe('should', function() {
       var aKey = {thisIs: 'anExampleObject'}
         , anotherKey = {doingThisBecauseOf: 'referential equality'}
         , testSet = new Set();
-      
+
       testSet.add(aKey);
       testSet.add(anotherKey);
 
@@ -1135,7 +1135,7 @@ describe('should', function() {
         , weirdSetKey2 = {toString: NaN}
         , weirdSetKey3 = []
         , weirdSet = new Set();
-      
+
       weirdSet.add(weirdSetKey1);
       weirdSet.add(weirdSetKey2);
 
@@ -1147,7 +1147,7 @@ describe('should', function() {
           , symSetKey2 = Symbol()
           , symSetKey3 = Symbol()
           , symSet = new Set();
-        
+
         symSet.add(symSetKey1);
         symSet.add(symSetKey2);
 
@@ -1393,103 +1393,103 @@ describe('should', function() {
 
     err(function(){
       (goodFn).should.throw();
-    }, /^expected \[Function(: goodFn)*\] to throw an error$/);
+    }, "expected [Function] to throw an error");
 
     err(function(){
       (goodFn).should.throw(ReferenceError);
-    }, /^expected \[Function(: goodFn)*\] to throw ReferenceError$/);
+    }, "expected [Function] to throw ReferenceError");
 
     err(function(){
       (goodFn).should.throw(specificError);
-    }, /^expected \[Function(: goodFn)*\] to throw 'RangeError: boo'$/);
+    }, "expected [Function] to throw 'RangeError: boo'");
 
     err(function(){
       (badFn).should.not.throw();
-    }, /^expected \[Function(: badFn)*\] to not throw an error but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw an error but 'Error: testing' was thrown");
 
     err(function(){
       (badFn).should.throw(ReferenceError);
-    }, /^expected \[Function(: badFn)*\] to throw 'ReferenceError' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'ReferenceError' but 'Error: testing' was thrown");
 
     err(function(){
       (badFn).should.throw(specificError);
-    }, /^expected \[Function(: badFn)*\] to throw 'RangeError: boo' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'RangeError: boo' but 'Error: testing' was thrown");
 
     err(function(){
       (badFn).should.not.throw(Error);
-    }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown");
 
     err(function(){
       (stringErrFn).should.not.throw();
-    }, /^expected \[Function(: stringErrFn)*\] to not throw an error but 'testing' was thrown$/);
+    }, "expected [Function] to not throw an error but 'testing' was thrown");
 
     err(function(){
       (stringErrFn).should.throw(ReferenceError);
-    }, /^expected \[Function(: stringErrFn)*\] to throw 'ReferenceError' but 'testing' was thrown$/);
+    }, "expected [Function] to throw 'ReferenceError' but 'testing' was thrown");
 
     err(function(){
       (stringErrFn).should.throw(specificError);
-    }, /^expected \[Function(: stringErrFn)*\] to throw 'RangeError: boo' but 'testing' was thrown$/);
+    }, "expected [Function] to throw 'RangeError: boo' but 'testing' was thrown");
 
     err(function(){
       (stringErrFn).should.not.throw('testing');
-    }, /^expected \[Function(: stringErrFn)*\] to throw error not including 'testing'$/);
+    }, "expected [Function] to throw error not including 'testing'");
 
     err(function(){
       (refErrFn).should.not.throw(ReferenceError);
-    }, /^expected \[Function(: refErrFn)*\] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown$/);
+    }, "expected [Function] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown");
 
     err(function(){
       (badFn).should.throw(PoorlyConstructedError);
-    }, /^expected \[Function(: badFn)*\] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown")
 
     err(function(){
       (ickyErrFn).should.not.throw(PoorlyConstructedError);
-    }, /^(expected \[Function(: ickyErrFn)*\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
+    }, /^(expected \[Function\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
 
     err(function(){
       (ickyErrFn).should.throw(ReferenceError);
-    }, /^(expected \[Function(: ickyErrFn)*\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
+    }, /^(expected \[Function\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/);
 
     err(function(){
       (specificErrFn).should.throw(new ReferenceError('eek'));
-    }, /^expected \[Function(: specificErrFn)*\] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown$/);
+    }, "expected [Function] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown");
 
     err(function(){
       (specificErrFn).should.not.throw(specificError);
-    }, /^expected \[Function(: specificErrFn)*\] to not throw 'RangeError: boo'$/);
+    }, "expected [Function] to not throw 'RangeError: boo'");
 
     err(function (){
       (badFn).should.not.throw(/testing/);
-    }, /^expected \[Function(: badFn)*\] to throw error not matching \/testing\/$/);
+    }, "expected [Function] to throw error not matching /testing/");
 
     err(function () {
       (badFn).should.throw(/hello/);
-    }, /^expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got \'testing\'$/);
+    }, "expected [Function] to throw error matching /hello/ but got \'testing\'");
 
     err(function () {
       (badFn).should.throw(Error, /hello/, 'blah');
-    }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/);
+    }, "blah: expected [Function] to throw error matching /hello/ but got 'testing'");
 
     err(function () {
       (badFn).should.throw(Error, 'hello', 'blah');
-    }, /^blah: expected \[Function(: badFn)*\] to throw error including 'hello' but got 'testing'$/);
+    }, "blah: expected [Function] to throw error including 'hello' but got 'testing'");
 
     err(function () {
       (customErrFn).should.not.throw();
-    }, /^expected \[Function(: customErrFn)*\] to not throw an error but 'CustomError: foo' was thrown$/);
+    }, "expected [Function] to not throw an error but 'CustomError: foo' was thrown");
 
     err(function(){
       (badFn).should.not.throw(Error, 'testing');
-    }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error: testing' was thrown");
 
     err(function(){
       (emptyStringErrFn).should.not.throw(Error, '');
-    }, /^expected \[Function(: emptyStringErrFn)*\] to not throw 'Error' but 'Error' was thrown$/);
+    }, "expected [Function] to not throw 'Error' but 'Error' was thrown");
 
     err(function(){
       (emptyStringErrFn).should.not.throw('');
-    }, /^expected \[Function(: emptyStringErrFn)*\] to throw error not including ''$/);
+    }, "expected [Function] to throw error not including ''");
   });
 
   it('respondTo', function(){
@@ -1525,7 +1525,7 @@ describe('should', function() {
 
     err(function(){
       (2).should.satisfy(matcher, 'blah');
-    }, /^blah: expected 2 to satisfy \[Function(: matcher)*\]$/);
+    }, "blah: expected 2 to satisfy [Function]");
   });
 
   it('closeTo', function(){
@@ -1742,7 +1742,7 @@ describe('should', function() {
   });
 
   it('increase, decrease', function() {
-    var obj = { value: 10 },
+    var obj = { value: 10, noop: null },
         arr = ['one', 'two'],
         pFn   = function() { arr.push('three') },
         popFn = function() { arr.pop() },
@@ -1778,6 +1778,13 @@ describe('should', function() {
 
     popFn.should.decrease(lenFn).by(1);
     popFn.should.decrease(lenFn).but.not.by(2);
+
+    err(function() {
+      incFn.should.increase(obj, 'noop');
+    }, 'expected null to be a number');
+    err(function() {
+      incFn.should.decrease(obj, 'noop');
+    }, 'expected null to be a number');
   });
 
   it('extensible', function() {