Skip to content
Browse files

Support for gently.expect(fn)

Awesome for testing callbacks. Also cleaned up some other code
responsible for showing pretty error messages.
  • Loading branch information...
1 parent df82091 commit 7d8de1c2e4e1d62e9eccbf482e2486d3a94d9b77 @felixge committed May 22, 2010
Showing with 88 additions and 27 deletions.
  1. +8 −0 Readme.md
  2. +38 −17 lib/gently/gently.js
  3. +1 −1 test/common.js
  4. +41 −9 test/simple/test-gently.js
View
8 Readme.md
@@ -67,6 +67,14 @@ Creates a new gently instance. It listens to the process `'exit'` event to make
Creates an expectation for an objects method to be called. You can optionally specify the call `count` you are expecting, as well as `mock` function that will run instead of the original function.
+#### gently.expect([count], mock)
+
+Returns a function that is supposed to be executed `count` times, delegating any calls to the provided `mock` function. Naming your mock closure will help to properly diagnose errors that are being thrown:
+
+ childProcess.exec('ls', gently.expect(function lsCallback(code) {
+ assert.equal(0, code);
+ }));
+
#### gently.restore(obj, method)
Restores an object method that has been previously overwritten using `gently.expect()`.
View
55 lib/gently/gently.js
@@ -12,21 +12,49 @@ Gently.prototype.toString = function() {
};
Gently.prototype.expect = function(obj, method, count, mock) {
- if (typeof count == 'function') {
+ var name;
+ if (typeof obj == 'function') {
+ mock = obj;
+ obj = null;
+ method = null;
+ count = 1;
+ } else if (typeof method == 'function') {
+ count = obj;
+ mock = method;
+ obj = null;
+ method = null;
+ } else if (typeof count == 'function') {
mock = count;
count = 1;
} else if (count === undefined) {
count = 1;
}
+ if (obj) {
+ name = obj.toString()+'.'+method+'()';
+ } else {
+ if (mock.name) {
+ name = mock.name+'()';
+ } else {
+ name = '>> '+mock.toString()+' <<';
+ }
+ }
+
while (count-- > 0) {
- this.expectations.push({obj: obj, method: method, mock: mock});
+ this.expectations.push({obj: obj, method: method, mock: mock, name: name});
+ }
+
+ var self = this;
+ function delegate() {
+ return self._mock(this, obj, method, name, Array.prototype.slice.call(arguments));
+ }
+
+ if (!obj) {
+ return delegate;
}
- var self = this, original = obj[method];
- obj[method] = function() {
- return self._mock(this, obj, method, Array.prototype.slice.call(arguments));
- };
+ var original = obj[method];
+ obj[method] = delegate;
obj[method]._original = original;
};
@@ -44,30 +72,23 @@ Gently.prototype.verify = function(msg) {
var expectation = this.expectations[0];
throw new Error
- ( 'Expected call to '+expectation.obj.toString()+'.'+expectation.method
- + ' did not happen'
+ ( 'Expected call to '+expectation.name+' did not happen'
+ ( (msg)
? ' ('+msg+')'
: ''
)
);
};
-Gently.prototype._mock = function(self, obj, method, args) {
+Gently.prototype._mock = function(self, obj, method, name, args) {
var expectation = this.expectations.shift();
if (!expectation) {
- throw new Error
- ( 'Unexpected call to '+obj.toString()+'.'+method+'(),'
- + ' no call was expected'
- );
+ throw new Error('Unexpected call to '+name+', no call was expected');
}
if (expectation.obj !== obj || expectation.method !== method) {
this.expectations.unshift(expectation);
- throw new Error
- ( 'Unexpected call to '+obj.toString()+'.'+method+'(), expected call to '
- + expectation.obj.toString()+'.'+expectation.method+'()'
- );
+ throw new Error('Unexpected call to '+name+', expected call to '+ expectation.name);
}
if (expectation.mock) {
View
2 test/common.js
@@ -4,5 +4,5 @@ var path = require('path')
require.paths.unshift(path.dirname(__dirname)+'/lib');
global.puts = sys.puts;
-global.p = sys.p;
+global.p = function() {sys.puts(sys.inspect.apply(null, arguments))};;
global.assert = require('assert');
View
50 test/simple/test-gently.js
@@ -16,7 +16,7 @@ test(function toString() {
assert.deepEqual(gently.toString(), '[Gently]');
});
-test(function expect() {
+test(function expectObjMethod() {
var OBJ = {};
OBJ.foo = function(x) {
return x;
@@ -33,6 +33,7 @@ test(function expect() {
assert.strictEqual(expectation.obj, OBJ);
assert.strictEqual(expectation.method, 'foo');
assert.strictEqual(expectation.mock, mock);
+ assert.strictEqual(expectation.name, OBJ.toString()+'.foo()');
assert.strictEqual(OBJ.foo._original, original);
})();
@@ -47,18 +48,49 @@ test(function expect() {
})();
var mockCalled = 0, SELF = {};
- gently._mock = function(self, obj, method, args) {
+ gently._mock = function(self, obj, method, name, args) {
mockCalled++;
assert.strictEqual(self, SELF);
assert.strictEqual(obj, OBJ);
assert.strictEqual(method, 'foo');
+ assert.strictEqual(name, gently.expectations[0].name);
assert.deepEqual(args, [1, 2]);
return 23;
};
assert.equal(OBJ.foo.apply(SELF, [1, 2]), 23);
assert.equal(mockCalled, 1);
});
+test(function expectClosure() {
+ function closureFn() {}
+
+ var fn = gently.expect(closureFn);
+ assert.equal(gently.expectations.length, 1);
+ var expectation = gently.expectations[0];
+ assert.strictEqual(expectation.obj, null);
+ assert.strictEqual(expectation.method, null);
+ assert.strictEqual(expectation.mock, closureFn);
+ assert.strictEqual(expectation.name, 'closureFn()');
+
+ var mockCalled = 0, SELF = {};
+ gently._mock = function(self, obj, method, name, args) {
+ mockCalled++;
+ assert.strictEqual(self, SELF);
+ assert.strictEqual(obj, null);
+ assert.strictEqual(method, null);
+ assert.strictEqual(name, 'closureFn()');
+ assert.deepEqual(args, [1, 2]);
+ return 23;
+ };
+ assert.equal(fn.apply(SELF, [1, 2]), 23);
+ assert.equal(mockCalled, 1);
+
+ var noName = function() {return a+a};
+ gently.expect(2, noName);
+ assert.equal(gently.expectations.length, 3);
+ assert.equal(gently.expectations[1].name, '>> '+noName.toString()+' <<');
+});
+
test(function restore() {
var OBJ = {};
OBJ.foo = function(x) {
@@ -94,24 +126,24 @@ test(function mock() {
return x * 2;
});
- assert.equal(gently._mock(SELF, OBJ1, 'foo', [5]), 10);
+ assert.equal(gently._mock(SELF, OBJ1, 'foo', 'dummy_name', [5]), 10);
(function testNoMoreCallExpected() {
try {
- gently._mock(SELF, OBJ1, 'foo', [5]);
+ gently._mock(SELF, OBJ1, 'foo', 'dummy_name', [5]);
assert.ok(false, 'throw needs to happen');
} catch (e) {
- assert.equal(e.message, 'Unexpected call to [OBJ 1].foo(), no call was expected');
+ assert.equal(e.message, 'Unexpected call to dummy_name, no call was expected');
}
})();
(function testDifferentCallExpected() {
gently.expect(OBJ2, 'bar');
try {
- gently._mock(SELF, OBJ1, 'foo', [5]);
+ gently._mock(SELF, OBJ1, 'foo', 'dummy_name', [5]);
assert.ok(false, 'throw needs to happen');
} catch (e) {
- assert.equal(e.message, 'Unexpected call to [OBJ 1].foo(), expected call to [OBJ 2].bar()');
+ assert.equal(e.message, 'Unexpected call to dummy_name, expected call to [OBJ 2].bar()');
}
assert.equal(gently.expectations.length, 1);
@@ -132,14 +164,14 @@ test(function verify() {
gently.verify();
assert.ok(false, 'throw needs to happen');
} catch (e) {
- assert.equal(e.message, 'Expected call to [OBJ].foo did not happen');
+ assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen');
}
try {
gently.verify('foo');
assert.ok(false, 'throw needs to happen');
} catch (e) {
- assert.equal(e.message, 'Expected call to [OBJ].foo did not happen (foo)');
+ assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen (foo)');
}
});

0 comments on commit 7d8de1c

Please sign in to comment.
Something went wrong with that request. Please try again.