簡體   English   中英

如何為express.static模擬http.ServerResponse和http.IncomingMessage

[英]How to mock http.ServerResponse and http.IncomingMessage for express.static

我測試自己的路由處理程序沒有問題,但在這種情況下我想測試express的靜態處理程序。 我無法為我的生活弄清楚為什么它會懸掛。 顯然,我缺少一些回調或者我需要發出一些事件。

我盡力做出最小的例子。

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();
    }

  });

});

設定,

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

它只是超時了。

我錯過了什么?

我也嘗試將請求設為可讀流。 沒變

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創建一個可讀流。 它確實有效

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();
    }    
  });

我想也許這是關於表達等待輸入流完成的東西,但這似乎也不是。 如果我使用響應使用模擬輸入流,它就可以正常工作

  it('test msg->res', function(done) {
    var req = new MockMessage({});
    var res = new MockResponse(responseDone);
    req.pipe(res);

    function responseDone() {
      console.log("done");
      done();
    }    
  });

任何有關我可能缺少的東西都會有所幫助

注意:雖然對第三方模擬庫的建議表示贊賞但我仍然真的希望了解我自己缺少的東西。 即使我最終切換到某個庫,我仍然想知道為什么這不起作用。

我發現了兩個阻止執行finish回調的問題。

  1. serve-static使用send模塊,該模塊用於從路徑創建文件讀取流並將其傳遞給res對象。 但該模塊使用on-finished模塊檢查響應對象中是否將finished屬性設置為false,否則會破壞文件讀取流 因此,文件流永遠不會有機會發出數據事件。

  2. 表達式初始化會覆蓋響應對象原型。 因此,http響應原型會覆蓋end()方法等默認流方法:

     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一起使用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;

};

看來我的答案並不完整。 由於某種原因,應用程序僅在找不到文件時才起作用。 首先要調試的是在shell(或cmd)中執行以下操作:

export DEBUG=express:router,send

然后運行測試,你會得到更多的信息。

與此同時,我仍然在研究這個問題,現在,請忽略我的答案。

-----------忽略這個,直到我確認它確實有效-----------

似乎表達靜態並不支持你給它的絕對路徑(__dirname)。

嘗試:

app.use(express.static('.'));

它會起作用。 請注意,您當前的mocha跑步者的目錄是'test /'

我不得不承認這是一個相當神秘的問題。 我通過這樣做嘗試'填補'它:

app.use(express.static(__dirname + '/../test')

但它仍然沒有用。 即使指定完整路徑也沒有解決這個問題。 奇怪。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM