2015-09-16 11 views
11

Nie miałem problemów z testowaniem własnych procedur obsługi tras, ale w tym przypadku chcę przetestować obsługę statyczną Express. Nie mogę dla mojego życia dowiedzieć się, dlaczego to wisi. Najwyraźniej brakuje jakiegoś wywołania zwrotnego lub jakiegoś zdarzenia, które muszę emitować.Jak wyśmiać http.ServerResponse i http.IncomingMessage dla express.static

Próbowałem zrobić najmniejszy przykład, jaki mogłem.

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 

to po prostu czasy zewnątrz.

Czego mi brakuje?

Próbowałem również uczynić żądanie strumieniem czytelnym. Bez zmian

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

    }); 

}); 

Wciąż kopałem. Zajrzyj do statycznego serwera, widzę, że tworzy czytelny strumień, dzwoniąc pod numer fs.createReadStream. Czyni skutecznie

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

Więc staramy się, że działa dobrze

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

Myślałem, że może to jest coś, o wyraźnej czeka na strumieniu wejściowym do końca, ale to nie wydaje się być albo. Gdybym zużywają makiety strumień wejściowy z odpowiedzią to działa dobrze

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

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

Każdy wgląd co I może być brakuje byłoby pomocne

Uwaga: podczas propozycje 3rd party bibliotek drwiących są doceniane nadal jestem Naprawdę chcę zrozumieć, czego mi brakuje, aby samemu to zrobić. Nawet jeśli w końcu przejdę do jakiejś biblioteki, nadal chcę wiedzieć, dlaczego to nie działa.

+0

Domyślam się, że to "przed" działa asynchronicznie. Może spróbuj użyć wywołania zwrotnego, aby poczekać na zakończenie? – brandonscript

+0

Nie jest to "przed" asynchroniczne. Ta część kończy się dobrze – gman

Odpowiedz

10

Znalazłem dwa problemy, które uniemożliwiają wykonanie wywołania zwrotnego finish.

  1. serve-static wykorzystuje send moduł, który jest używany do tworzenia pliku readstream od ścieżki i ją doprowadzić do res obiektu. Ale moduł ten używa modułu on-finished, który sprawdza, czy atrybut finished jest ustawiony na wartość false w obiekcie odpowiedzi, w przeciwnym razie jest to destroys the file readstream. Więc strumień plików nigdy nie ma szansy na emisję danych.

  2. express initialization zastępuje prototyp obiektu odpowiedzi. Więc metody domyślny strumień jak end() metody są zastępowane przez prototyp odpowiedzi http:

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

    Aby temu zapobiec, dodałem kolejne warstwy pośredniej tuż przed middleware statyczny zresetować go do MockResponse prototype:

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

Tutaj wprowadzono zmiany, aby działały z 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:

Jak @gman wskazał, że możliwe jest wykorzystanie właściwości bezpośredni zamiast metody prototypu. W takim przypadku dodatkowe oprogramowanie pośrednie do zastąpienia prototypu nie jest konieczne:

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

Dzięki. Nadal miałem problemy, których nie rozumiem, ale przynajmniej mam coś do roboty. Jako jeden z przykładów, dla których zmienię "write_" na bezpośrednią własność, nie powinienem martwić się zmianą prototypu ekspresu, ale to się psuje, ponieważ gdzieś indziej express oczekuje, że obiekt naprawdę jest ServerResponse odziedziczony z OutgoingMessage. Dlaczego to działa, gdy wstawiasz prototyp z powrotem, nie dostaję – gman

+0

Tak, własność bezpośrednia powinna działać. Co powiesz o innych metodach strumienia, takich jak 'end'? czy zrobiłeś to bezpośrednio? Ponieważ, kiedy testowałem, faktycznie dostałem błąd w metodzie "end", a nie na "_write". – hassansin

+0

@gman, dodano zmiany kodu dla bezpośredniej własności. – hassansin

3

Wygląda na to, że moja odpowiedź nie jest kompletna. Z jakiegoś powodu aplikacja działa tylko wtedy, gdy plik nie zostanie znaleziony. Pierwszą rzeczą do zrobienia jest debugowania następujące w Twojej powłoki (lub cmd):

export DEBUG=express:router,send 

następnie uruchomić test, dostaniesz więcej informacji.

Tymczasem wciąż się nad tym zastanawiam, na razie zignoruj ​​moją odpowiedź poniżej.

----------- zignorować to aż mi sprawdzić, czy działa -----------

Wygląda na to wyraźnej statyczne nie sprzyja bezwzględną ścieżkę dajesz (__nazwa).

Spróbuj:

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

i będzie działać. Zauważ, że twój aktualny katalog dla biegacza mokki to "test /"

Muszę przyznać, że to dość tajemnica. Próbowałem go "uzupełnić", wykonując:

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

ale nadal nie działało.Nawet określenie pełnej ścieżki nie rozwiązało tego problemu. Dziwne.

+1

Dowiedziałem się, że większość modułów ma tę opcję debugowania. Proponuję przeszukać katalog node_modules pod kątem require ('debug') i włączyć wszystkie opcje debugowania. To może dać ci wgląd w to, co się dzieje. Zauważyłem, że wyraźna wzmianka wspomina o tym, że nadaje on kierunek rozwoju, może mieć z tym coś wspólnego. Lista użytych flag debugowania: DEBUG = ekspresowe: router, wysyłanie, obsługa końcowa, ekspresowe: aplikacja, ekspresowe: router: warstwa, mokka: działające, ekspresowe: router: trasa, mokka: działające, mokka: biegacz, express: view Ostatnia rzecz, możesz wejść do katalogu node_modules i dodać komunikaty debugowania plików. – Meir

+1

Ponadto statyczny używa modułu o nazwie send. Jeśli się temu przyjrzysz, zobaczysz komentarz na temat tego, jak bałagan jest i że wymaga refaktoryzacji. – Meir