I want to proxy test results running on other environment in real time.
Here is some pseudo code that I want to make real:
var test = proxy.getCurrentTest();
// => {slow: 200, timeout: 2000, duration: 235, result: 'error'};
var tmpIt = it('test1', function(){
this.slow(test.slow);
this.timeout(test.timeout);
});
tmpIt.close({
duration: test.duration,
result: test.result
});
// this should make this test red in the output,
// because the `result` is not 'success'
Is it anyhow possible to set test's result and duration without "really" running it? And get all the visual mocha output to the terminal?
edit: this question is not about how to pass variables with the test results form the child process to the main process. it's already working for me.
Hope I understood the requirements correctly. What I implemented is a test result forwarder to mocha that integrated into mocha.
To integrate with mocha this implementation describes a custom mocha interface to proxy test results from tests executed in another environment.
To use this interface the -u
argument has to be passed to mocha when running mocha
> mocha -u ./path/to/proxy-interface ...
Note that ./path/to/proxy-interface
is the path that mocha uses in a require
call to require the interface module.
The proxy interface is responsible to expose a proxyTest
function to the global context such as mocha's BDD interface does with it
, invoke the passed function to get the test results and forward the test results while preserving the number of tests executed displayed by the test runner.
var Mocha = require('mocha');
var Suite = Mocha.Suite;
var Test = Mocha.Test;
var escapeRe = require('escape-string-regexp');
module.exports = function(suite) {
var suites = [suite];
suite.on('pre-require', function(context, file, mocha) {
// A bit hacky since we require mocha internal common interface module
var common = require('mocha/lib/interfaces/common')(suites, context);
context.run = mocha.options.delay && common.runWithSuite(suite);
context.proxyTest = function(title, fn) {
var suite = suites[0];
if (suite.pending) {
fn = null;
}
var test = new ProxyTest(title, fn);
test.file = file;
suite.addTest(test);
return test;
};
});
};
var Runnable = Mocha.Runnable;
var inherits = require('util').inherits;
function ProxyTest(title, fn) {
Runnable.call(this, title, null);
this.pending = !fn;
this.type = 'test';
this.body = (fn || '').toString();
this.fn = fn;
}
inherits(ProxyTest, Runnable);
ProxyTest.prototype.run = function(done) {
var proxiedTestResult = this.fn();
this.duration = proxiedTestResult.duration;
this.timedOut = this.timeout() > proxiedTestResult.timeout;
done(proxiedTestResult.result);
};
ProxyTest.prototype.clone = function() {
var test = new ProxyTest(this.title, this.fn);
test.timeout(this.timeout());
test.slow(this.slow());
test.enableTimeouts(this.enableTimeouts());
test.retries(this.retries());
test.currentRetry(this.currentRetry());
test.globals(this.globals());
test.parent = this.parent;
test.file = this.file;
test.ctx = this.ctx;
return test;
};
The code above overrides Mocha's Runnable
run implementation and runs the passed function to get test results and sets the required fields in the ProxyTest
interface to be compatible with mocha tests.
Usage
In your tests use the proxyTest
global to register a new test with mocha
var proxy = {
getErrorTestResult() {
return {slow: 200, timeout: 2000, duration: 50, result: 'error'};
},
getTimeoutTestResult() {
return {slow: 200, timeout: 2000, duration: 3000 };
},
getSlowTestResult() {
return {slow: 200, timeout: 2000, duration: 235 };
},
getSuccessTestResult() {
return {slow: 200, timeout: 2000, duration: 50 };
}
}
proxyTest('error', proxy.getErrorTestResult);
proxyTest('timeout', proxy.getTimeoutTestResult);
proxyTest('slow', proxy.getSlowTestResult);
proxyTest('success', proxy.getSuccessTestResult);
Output
Implications
The drawback of this approach is that a custom interface has to be passed to mocha AND that you cannot use mocha BDD vocabulary like describe
. The second drawback can be eliminated if you "extend" (In this case: copy some code) the BDD interface of mocha:
var Mocha = require('mocha');
/**
* Module dependencies.
*/
var Suite = Mocha.Suite;
var Test = Mocha.Test;
var escapeRe = require('escape-string-regexp');
/**
* BDD-style interface - extended with proxy functionality:
*
* describe('Array', function() {
* describe('#indexOf()', function() {
* it('should return -1 when not present', function() {
* // ...
* });
*
* it('should return the index when present', function() {
* // ...
* });
* });
* });
*
* @param {Suite} suite Root suite.
*/
module.exports = function(suite) {
var suites = [suite];
suite.on('pre-require', function(context, file, mocha) {
// A bit hacky since we require mocha internal common interface module
var common = require('mocha/lib/interfaces/common')(suites, context);
context.before = common.before;
context.after = common.after;
context.beforeEach = common.beforeEach;
context.afterEach = common.afterEach;
context.run = mocha.options.delay && common.runWithSuite(suite);
/**
* Describe a "suite" with the given `title`
* and callback `fn` containing nested suites
* and/or tests.
*/
context.describe = context.context = function(title, fn) {
var suite = Suite.create(suites[0], title);
suite.file = file;
suites.unshift(suite);
fn.call(suite);
suites.shift();
return suite;
};
/**
* Pending describe.
*/
context.xdescribe = context.xcontext = context.describe.skip = function(title, fn) {
var suite = Suite.create(suites[0], title);
suite.pending = true;
suites.unshift(suite);
fn.call(suite);
suites.shift();
};
/**
* Exclusive suite.
*/
context.describe.only = function(title, fn) {
var suite = context.describe(title, fn);
mocha.grep(suite.fullTitle());
return suite;
};
/**
* Describe a specification or test-case
* with the given `title` and callback `fn`
* acting as a thunk.
*/
var it = context.it = context.specify = function(title, fn) {
var suite = suites[0];
if (suite.pending) {
fn = null;
}
var test = new Test(title, fn);
test.file = file;
suite.addTest(test);
return test;
};
/**
* Exclusive test-case.
*/
context.it.only = function(title, fn) {
var test = it(title, fn);
var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return test;
};
/**
* Pending test case.
*/
context.xit = context.xspecify = context.it.skip = function(title) {
context.it(title);
};
/**
* Number of attempts to retry.
*/
context.it.retries = function(n) {
context.retries(n);
};
context.proxyTest = function(title, fn) {
var suite = suites[0];
if (suite.pending) {
fn = null;
}
var test = new ProxyTest(title, fn);
test.file = file;
suite.addTest(test);
return test;
};
});
};
var Runnable = Mocha.Runnable;
var inherits = require('util').inherits;
function ProxyTest(title, fn) {
Runnable.call(this, title, null);
this.pending = !fn;
this.type = 'test';
this.body = (fn || '').toString();
this.fn = fn;
}
inherits(ProxyTest, Runnable);
ProxyTest.prototype.run = function(done) {
var proxiedTestResult = this.fn();
this.duration = proxiedTestResult.duration;
this.timedOut = this.timeout() > proxiedTestResult.timeout;
done(proxiedTestResult.result);
};
ProxyTest.prototype.clone = function() {
var test = new ProxyTest(this.title, this.fn);
test.timeout(this.timeout());
test.slow(this.slow());
test.enableTimeouts(this.enableTimeouts());
test.retries(this.retries());
test.currentRetry(this.currentRetry());
test.globals(this.globals());
test.parent = this.parent;
test.file = this.file;
test.ctx = this.ctx;
return test;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With