Testowanie interfejsu API Mocha: uzyskanie „TypeError: app.address nie jest funkcją”

102

Mój problem

Zakodowałem bardzo prosty interfejs API CRUD i niedawno zacząłem kodować również niektóre testy przy użyciu chaii, chai-httpale mam problem podczas uruchamiania testów w programie $ mocha.

Po uruchomieniu testów w powłoce pojawia się następujący błąd:

TypeError: app.address is not a function

Mój kod

Oto próbka jednego z moich testów ( /tests/server-test.js ):

var chai = require('chai');
var mongoose = require('mongoose');
var chaiHttp = require('chai-http');
var server = require('../server/app'); // my express app
var should = chai.should();
var testUtils = require('./test-utils');

chai.use(chaiHttp);

describe('API Tests', function() {
  before(function() {
    mongoose.createConnection('mongodb://localhost/bot-test', myOptionsObj);
  });

  beforeEach(function(done) {
    // I do stuff like populating db
  });

  afterEach(function(done) {
    // I do stuff like deleting populated db
  });

  after(function() {
    mongoose.connection.close();
  });

  describe('Boxes', function() {

    it.only('should list ALL boxes on /boxes GET', function(done) {
      chai.request(server)
        .get('/api/boxes')
        .end(function(err, res){
          res.should.have.status(200);
          done();
        });
    });

    // the rest of the tests would continue here...

  });

});

Oraz moje expresspliki aplikacji ( /server/app.js ):

var mongoose = require('mongoose');
var express = require('express');
var api = require('./routes/api.js');
var app = express();

mongoose.connect('mongodb://localhost/db-dev', myOptionsObj);

// application configuration
require('./config/express')(app);

// routing set up
app.use('/api', api);

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('App listening at http://%s:%s', host, port);
});

i ( /server/routes/api.js ):

var express = require('express');
var boxController = require('../modules/box/controller');
var thingController = require('../modules/thing/controller');
var router = express.Router();

// API routing
router.get('/boxes', boxController.getAll);
// etc.

module.exports = router;

Dodatkowe notatki

Próbowałem wylogować się ze serverzmiennej w pliku /tests/server-test.js przed uruchomieniem testów:

...
var server = require('../server/app'); // my express app
...

console.log('server: ', server);
...

i wynik, który jest pusty obiekt: server: {}.

charliebrownie
źródło

Odpowiedzi:

228

Nie eksportujesz niczego w swoim module aplikacji. Spróbuj dodać to do swojego pliku app.js:

module.exports = server
xersiee
źródło
31
jedyny problem, jaki mam z tym, to to, że kod aplikacji nigdy nie powinien być zmieniany, aby pasował do testu.
dman
@chovy, czy rozwiązałeś ten problem? Mam poniżej alternatywę, która zadziałała dla mnie.
Skyguard
1
potencjalny problem dotyczy serwera, który zawiera usługi asynchroniczne, ponieważ chai-http nie zna tej logiki i będzie działać bezpośrednio, zanim serwer zostanie w pełni uruchomiony
atom2ueki
1
@Nabuska w tym przypadku serwer prawdopodobnie jest już ustawiony z app.listen (...)
Garr Godfrey
Do testów nie powinieneś używać app.listen (), który uruchamia rzeczywisty serwer, zamiast tego użyj http.createServer ()
Whyhankee
42

Ważne jest, aby wyeksportować http.Serverobiekt zwrócony przez, app.listen(3000)a nie tylko funkcję app, w przeciwnym razie otrzymasz TypeError: app.address is not a function.

Przykład:

index.js

const koa = require('koa');
const app = new koa();
module.exports = app.listen(3000);

index.spec.js

const request = require('supertest');
const app = require('./index.js');

describe('User Registration', () => {
  const agent = request.agent(app);

  it('should ...', () => {
Kim Kern
źródło
2
W swojej odpowiedzi powinieneś zawrzeć, dlaczego „ważne jest, aby wyeksportować http.Serverobiekt”.
GrumpyCrouton
@GrumpyCrouton Dodałem błąd, który otrzymasz w przeciwnym razie
Kim Kern
1
Dziękuję Kim. Dałem Ci +1, ponieważ myślę, że to poprawi Twoją odpowiedź. W przyszłości powinieneś wyjaśnić dlaczego . Wyobraź sobie, że ktoś patrzy na to pytanie i ma tylko podstawową wiedzę, dobrze przemyślana i wyjaśniona odpowiedź nauczy go o wiele więcej.
GrumpyCrouton
Dla testów Ci shoud nie używać app.listen (), która rozpoczyna rzeczywisty serwer, użyj http.createServer () zamiast
Whyhankee
28

Może to również pomóc i spełnia wymagania @dman dotyczące zmiany kodu aplikacji w celu dopasowania do testu.

w razie potrzeby wyślij żądanie do hosta lokalnego i portu chai.request('http://localhost:5000')

zamiast

chai.request(server)

naprawiło to ten sam komunikat o błędzie, który miałem przy użyciu Koa JS (v2) i ava js.

Skyguard
źródło
3
Po wykonaniu tego błędu nie nadchodzi, ale w pobieraniu danych nadchodzi null, a kod statusu 200 ok.
Anita Mehta
4

Powyższe odpowiedzi poprawnie rozwiązują problem: supertestchce http.Serverpracować. Jednak wywołanie w app.listen()celu uzyskania serwera spowoduje również uruchomienie serwera nasłuchującego, jest to zła praktyka i niepotrzebne.

Możesz to obejść, używając http.createServer():

import * as http from 'http';
import * as supertest from 'supertest';
import * as test from 'tape';
import * as Koa from 'koa';

const app = new Koa();

# add some routes here

const apptest = supertest(http.createServer(app.callback()));

test('GET /healthcheck', (t) => {
    apptest.get('/healthcheck')
    .expect(200)
    .expect(res => {
      t.equal(res.text, 'Ok');
    })
    .end(t.end.bind(t));
});
Dlaczegohankee
źródło
1

Mieliśmy ten sam problem, gdy uruchamialiśmy mokkę przy użyciu ts-node w naszym projekcie bezserwerowym node + Typecript.

Nasz tsconfig.json miał "sourceMap": true. Tak wygenerowane pliki .js i .js.map powodują pewne zabawne problemy z transpilacją (podobne do tego). Kiedy uruchamiamy mokkę runner za pomocą ts-node. Więc ustawię flagę sourceMap na false i usunę wszystkie pliki .js i .js.map w naszym katalogu src. Wtedy problem zniknął.

Jeśli już wygenerowałeś pliki w swoim folderze src, poniższe polecenia byłyby naprawdę pomocne.

find src -name " .js.map" -exec rm {} \; find src -name " .js" -exec rm {} \;

Dilunika
źródło
0

Na wszelki wypadek, jeśli ktoś używa Hapijs, problem nadal występuje, ponieważ nie używa on Express.js, więc funkcja address () nie istnieje.

TypeError: app.address is not a function
      at serverAddress (node_modules/chai-http/lib/request.js:282:18)

Sposób obejścia tego problemu

// this makes the server to start up
let server = require('../../server')

// pass this instead of server to avoid error
const API = 'http://localhost:3000'

describe('/GET token ', () => {
    it('JWT token', (done) => {
       chai.request(API)
         .get('/api/token?....')
         .end((err, res) => {
          res.should.have.status(200)
          res.body.should.be.a('object')
          res.body.should.have.property('token')
          done()
      })
    })
  })
Leonardo Ampuero
źródło