如何为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议表示赞赏,但我还是非常想知道我错过了什么。 即使我最终转换到一些图书馆,我仍然想知道为什么这不起作用。

我发现两个问题阻止执行finishcallback。

  1. serve-static使用send模块,用于从path创build文件读取stream并将其传递给res对象。 但是该模块使用on-finished模块来检查响应对象中的finished属性是否设置为false,否则会破坏文件读取stream 。 所以文件stream从来没有机会发出数据事件。

  2. 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也没有解决这个问题。 奇怪。