如何为express.static模拟http.ServerResponse和http.IncomingMessage
我没有麻烦testing自己的路由处理程序,但在这种情况下,我想testing快递的静态处理程序。 我不能为了我的生活找出它为什么挂。 显然有一些callback我失踪或一些事件,我需要发出。
我尽力做出最小的例子。
var events = require('events'); var express = require('express'); var stream = require('stream'); var util = require('util'); function MockResponse(callback) { stream.Writable.call(this); this.headers = {}; this.statusCode = -1; this.body = undefined; this.setHeader = function(key, value) { this.headers[key] = value; }.bind(this); this.on('finish', function() { console.log("finished response"); callback(); }); }; util.inherits(MockResponse, stream.Writable); MockResponse.prototype._write = function(chunk, encoding, done) { if (this.body === undefined) { this.body = ""; } this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined); done(); }; function createRequest(req) { var emitter = new events.EventEmitter(); req.on = emitter.on.bind(emitter); req.once = emitter.once.bind(emitter); req.addListener = emitter.addListener.bind(emitter); req.emit = emitter.emit.bind(emitter); return req; }; describe('test', function() { var app; before(function() { app = express(); app.use(express.static(__dirname)); }); it('gets test.js', function(done) { var req = createRequest({ url: "http://foo.com/test.js", method: 'GET', headers: { }, }); var res = new MockResponse(responseDone); app(req, res); function responseDone() { console.log("done"); done(); } }); });
build立,
mkdir foo cd foo mkdir test cat > test/test.js # copy and paste code above ^D npm install express npm install mocha node node_modules/mocha/bin/mocha --recursive
它只是超时。
我错过了什么?
我也试图使请求一个可读的stream。 不用找了
var events = require('events'); var express = require('express'); var stream = require('stream'); var util = require('util'); function MockResponse(callback) { stream.Writable.call(this); this.headers = {}; this.statusCode = -1; this.body = undefined; this.setHeader = function(key, value) { this.headers[key] = value; }.bind(this); this.on('finish', function() { console.log("finished response"); callback(); }); }; util.inherits(MockResponse, stream.Writable); MockResponse.prototype._write = function(chunk, encoding, done) { if (this.body === undefined) { this.body = ""; } this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined); done(); }; function MockMessage(req) { stream.Readable.call(this); var self = this; Object.keys(req).forEach(function(key) { self[key] = req[key]; }); } util.inherits(MockMessage, stream.Readable); MockMessage.prototype._read = function() { this.push(null); }; describe('test', function() { var app; before(function() { app = express(); app.use(express.static(__dirname)); }); it('gets test.js', function(done) { var req = new MockMessage({ url: "http://foo.com/test.js", method: 'GET', headers: { }, }); var res = new MockResponse(responseDone); app(req, res); function responseDone() { console.log("done"); done(); } }); });
我还在挖。 看看里面的静态服务器,我看到它通过调用fs.createReadStream
创build一个可读的stream。 它确实有效
var s = fs.createReadStream(filename); s.pipe(res);
所以试图让自己工作得很好
it('test stream', function(done) { var s = fs.createReadStream(__dirname + "/test.js"); var res = new MockResponse(responseDone); s.pipe(res); function responseDone() { console.log("done"); done(); } });
我想也许这是关于快速等待inputstream完成的东西,但似乎也不是这样。 如果我使用响应模拟inputstream,它工作得很好
it('test msg->res', function(done) { var req = new MockMessage({}); var res = new MockResponse(responseDone); req.pipe(res); function responseDone() { console.log("done"); done(); } });
任何我可能会缺less的洞察力将会有所帮助
注意:虽然第三方嘲笑图书馆的build议表示赞赏,但我还是非常想知道我错过了什么。 即使我最终转换到一些图书馆,我仍然想知道为什么这不起作用。
我发现两个问题阻止执行finish
callback。
-
serve-static
使用send
模块,用于从path创build文件读取stream并将其传递给res
对象。 但是该模块使用on-finished
模块来检查响应对象中的finished
属性是否设置为false,否则会破坏文件读取stream 。 所以文件stream从来没有机会发出数据事件。 -
expression式初始化会覆盖响应对象的原型。 所以像
end()
方法这样的默认stream方法被http响应原型所覆盖:exports.init = function(app){ return function expressInit(req, res, next){ ... res.__proto__ = app.response; .. }; };
为了防止这种情况,我在静态中间件之前添加了另一个中间件,将其重置为
MockResponse
原型:app.use(function(req, res, next){ res.__proto__ = MockResponse.prototype; //change it back to MockResponse prototype next(); });
以下是为了使其与MockResponse
工作所做的更改:
... function MockResponse(callback) { ... this.finished = false; // so `on-finished` module doesn't emit finish event prematurely //required because of 'send' module this.getHeader = function(key) { return this.headers[key]; }.bind(this); ... }; ... describe('test', function() { var app; before(function() { app = express(); //another middleware to reset the res object app.use(function(req, res, next){ res.__proto__ = MockResponse.prototype; next(); }); app.use(express.static(__dirname)); }); ... });
编辑:
正如@gman指出的那样,可以使用直接属性而不是原型方法。 在这种情况下,覆盖原型的额外中间件不是必需的:
function MockResponse(callback) { ... this.finished = false; // so `on-finished` module doesn't emit finish event prematurely //required because of 'send' module this.getHeader = function(key) { return this.headers[key]; }.bind(this); ... //using direct property for _write, write, end - since all these are changed when prototype is changed this._write = function(chunk, encoding, done) { if (this.body === undefined) { this.body = ""; } this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined); done(); }; this.write = stream.Writable.prototype.write; this.end = stream.Writable.prototype.end; };
看来我的答案并不完整。 出于某种原因,该应用程序只有在找不到该文件的情况下才有效 首先要debugging的是在shell(或cmd)中执行以下操作:
export DEBUG=express:router,send
然后运行testing,你会得到更多的信息。
与此同时,我仍在研究这一点,现在,忽略我的答案。
———–忽略这个,直到我确认它工作———–
看起来像expression静态不赞成你给它的绝对path(__dirname)。
尝试:
app.use(express.static('.'));
它会工作。 请注意,您当前的摩卡跑者目录是'test /'
我不得不承认这是相当的神秘。 我试着“充实”它:
app.use(express.static(__dirname + '/../test')
但仍然没有奏效。 即使指定完整path也没有解决这个问题。 奇怪。