2015-09-16 15 views
11

Tôi đã không gặp khó khăn khi kiểm tra trình xử lý tuyến đường của riêng tôi nhưng trong trường hợp này tôi muốn thử nghiệm trình xử lý tĩnh của express. Tôi không thể cho cuộc sống của tôi tìm ra lý do tại sao nó treo. Rõ ràng có một số gọi lại tôi đang thiếu hoặc một số sự kiện tôi cần phải phát ra.Làm thế nào để giả http.ServerResponse và http.IncomingMessage cho express.static

Tôi đã cố tạo ví dụ nhỏ nhất có thể.

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

    }); 

}); 

Setup,

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 

nó chỉ lần ra ngoài.

Tôi đang thiếu gì?

Tôi cũng đã thử thực hiện yêu cầu một luồng có thể đọc được. Không thay đổi

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

    }); 

}); 

Tôi vẫn đang đào. Nhìn vào bên trong máy chủ tĩnh Tôi thấy nó tạo ra một dòng có thể đọc được bằng cách gọi fs.createReadStream. Nó thực hiện một cách hiệu quả

var s = fs.createReadStream(filename); 
s.pipe(res); 

Vì vậy, cố gắng mà bản thân mình làm việc tốt

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

Tôi nghĩ có lẽ đó là một cái gì đó về nhanh chờ đợi cho các dòng đầu vào để kết thúc nhưng điều đó dường như không thể là một trong hai. Nếu tôi tiêu thụ các dòng đầu vào giả với câu trả lời nó làm việc tốt

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

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

Bất kỳ cái nhìn sâu sắc những gì tôi có thể bị mất sẽ là hữu ích

Lưu ý: trong khi gợi ý cho các thư viện chế giễu bên thứ 3 được đánh giá cao tôi vẫn thực sự tìm hiểu những gì tôi đang thiếu để tự mình làm. Ngay cả khi tôi cuối cùng chuyển sang một số thư viện, tôi vẫn muốn biết tại sao điều này không hiệu quả.

+0

Tôi đoán đó là 'trước' chạy không đồng bộ. Có thể thử sử dụng gọi lại để đợi nó hoàn thành? – brandonscript

+0

Nó không phải là 'trước' chạy async. Phần đó hoàn thành tốt – gman

Trả lời

10

Tôi đã tìm thấy hai vấn đề ngăn không cho việc gọi lại finish bị thực thi.

  1. serve-static sử dụng send mô-đun được sử dụng để tạo ra tập tin readstream từ đường dẫn và ống nó để res đối tượng. Nhưng mô-đun đó sử dụng mô-đun on-finished để kiểm tra xem thuộc tính finished được đặt thành sai trong đối tượng phản hồi hay không, nếu không thì destroys the file readstream. Vì vậy, dòng phim không bao giờ có cơ hội phát ra sự kiện dữ liệu.

  2. thể hiện initialization ghi đè mẫu thử nghiệm đối tượng phản hồi. Vì vậy, các phương pháp luồng mặc định như end() phương pháp được ghi đè bởi nguyên mẫu phản ứng http:

    exports.init = function(app){ 
        return function expressInit(req, res, next){ 
        ... 
        res.__proto__ = app.response; 
        .. 
        }; 
    }; 
    

    Để ngăn chặn điều này, tôi đã thêm middleware khác ngay trước middleware tĩnh để thiết lập lại nó trở lại MockResponse nguyên mẫu:

    app.use(function(req, res, next){ 
        res.__proto__ = MockResponse.prototype; //change it back to MockResponse prototype 
        next(); 
    }); 
    

Dưới đây là các thay đổi được thực hiện để làm cho nó hoạt động với 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)); 
    }); 

    ... 

}); 

EDIT:

Như @gman chỉ ra, người ta có thể sử dụng tài sản trực tiếp thay vì phương pháp nguyên mẫu. Trong trường hợp đó, phần mềm trung gian thừa để ghi đè lên mẫu thử nghiệm là không cần thiết:

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; 

}; 
+0

Cảm ơn. Tôi vẫn gặp rất nhiều vấn đề mà tôi không hiểu hết nhưng ít nhất tôi cũng có một vài thứ để làm việc. Như một ví dụ cho tôi thay đổi 'write_' làm một thuộc tính trực tiếp sau đó tôi không cần phải quan tâm đến sự thay đổi nguyên mẫu của express nhưng nó phá vỡ bởi vì một nơi khác thể hiện hy vọng đối tượng thực sự là một ServerResponse được thừa kế từ OutgoingMessage. Tại sao nó hoạt động khi bạn đưa mẫu thử trở lại tho Tôi không nhận được – gman

+0

Có, tài sản trực tiếp sẽ hoạt động. Làm thế nào về các phương pháp khác của dòng, như 'kết thúc'? bạn đã làm cho nó trực tiếp tài sản? Bởi vì, khi tôi thử nghiệm tôi thực sự có một lỗi trên phương thức 'kết thúc' không phải trên' _write'. – hassansin

+0

@gman, đã thêm các thay đổi mã cho thuộc tính trực tiếp. – hassansin

3

Có vẻ câu trả lời của tôi chưa hoàn thành. Vì một số lý do, ứng dụng chỉ hoạt động nếu không tìm thấy tệp. Điều đầu tiên để gỡ lỗi là làm như sau trong shell của bạn (hoặc cmd):

export DEBUG=express:router,send 

sau đó chạy thử nghiệm, bạn sẽ nhận được thêm thông tin.

Trong khi đó, tôi vẫn đang xem xét điều này, bây giờ, bỏ qua câu trả lời của tôi bên dưới.

----------- bỏ qua điều này cho đến khi tôi xác minh rằng nó không làm việc -----------

Nó có vẻ như tĩnh nhanh không ủng hộ đường dẫn tuyệt đối bạn cho nó (__dirname).

Hãy thử:

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

và nó sẽ làm việc. Lưu ý rằng thư mục hiện tại của bạn cho nhân vật mocha là 'test /'

Tôi phải thừa nhận đây là một điều rất quan trọng. Tôi đã thử 'làm đầy' bằng cách thực hiện:

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

nhưng vẫn không hoạt động.Ngay cả việc xác định đường dẫn đầy đủ cũng không giải quyết được điều này. Lạ thật.

+1

Tôi phát hiện ra rằng hầu hết các mô-đun đều có tùy chọn gỡ lỗi đó. Tôi đề nghị bạn tìm kiếm thư mục node_modules của bạn cho yêu cầu ('debug') và bật tất cả các tùy chọn gỡ lỗi. Nó có thể cung cấp cho bạn một cái nhìn sâu sắc vào những gì diễn ra. Tôi đã nhận thấy rằng thể hiện đề cập đến nó đặt env để phát triển, nó có thể có một cái gì đó để làm với nó. Danh sách cờ gỡ lỗi tôi đã sử dụng là: DEBUG = express: router, gửi, finalhandler, express: application, express: router: lớp, mocha: runnable, express: router: route, mocha: runnable, mocha: runner, thể hiện: xem Một điều cuối cùng, bạn có thể vào thư mục node_modules và thêm vào các thông báo gỡ lỗi tệp – Meir

+1

Ngoài ra, tĩnh sử dụng mô-đun được gọi là gửi. Nếu bạn nhìn vào nó, bạn sẽ thấy có một nhận xét về sự lộn xộn của nó và nó đòi hỏi tái cấu trúc. – Meir

Các vấn đề liên quan