Teste de API do Mocha: recebendo 'TypeError: app.address não é uma função'

102

Meu problema

Eu codifiquei uma API CRUD muito simples e comecei recentemente a codificar também alguns testes usando chaie, chai-httpmas estou tendo um problema ao executar meus testes com $ mocha.

Quando executo os testes, recebo o seguinte erro no shell:

TypeError: app.address is not a function

Meu código

Aqui está um exemplo de um dos meus testes ( /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...

  });

});

E meus expressarquivos de aplicativo ( /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);
});

e ( /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;

Notas extras

Tentei desconectar a servervariável no arquivo /tests/server-test.js antes de executar os testes:

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

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

e I o resultado de que é um objecto vazio: server: {}.

charliebrownie
fonte

Respostas:

228

Você não exporta nada em seu módulo de app. Tente adicionar isso ao seu arquivo app.js:

module.exports = server
xersiee
fonte
31
o único problema que tenho com isso é que o código do aplicativo nunca deve ser alterado para se ajustar a um teste.
dman
@chovy você resolveu isso? Tenho uma alternativa abaixo que funcionou para mim.
Skyguard
1
o problema potencial é lidar com o servidor que contém serviços assíncronos, porque o chai-http não conhece a lógica disso e será executado diretamente antes mesmo de seu servidor iniciar totalmente
atom2ueki
1
@Nabuska, neste caso, o servidor provavelmente já está configurado com app.listen (...)
Garr Godfrey
Para seus testes, você não deve usar app.listen (), que inicia um servidor real, use http.createServer () em vez
Whyhankee
42

É importante exportar o http.Serverobjeto retornado por em app.listen(3000)vez de apenas a função app, caso contrário, você obterá TypeError: app.address is not a function.

Exemplo:

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
fonte
2
Você deve incluir em sua resposta porque é "importante exportar o http.Serverobjeto".
GrumpyCrouton
@GrumpyCrouton Eu adicionei o erro que você obterá do contrário
Kim Kern
1
Obrigado Kim. Eu lhe dei +1 porque acho que isso melhora sua resposta. No futuro, você deve explicar o porquê . Imagine alguém olhando para esta questão e só tem conhecimento básico, uma resposta bem pensada e explicada vai lhe ensinar muito mais.
GrumpyCrouton
Para seus testes, você não deve usar app.listen () que inicia um servidor real, use http.createServer () em vez
Whyhankee
28

Isso também pode ajudar e satisfazer o ponto @dman de alterar o código do aplicativo para ajustar um teste.

faça sua solicitação ao host local e à porta conforme necessário chai.request('http://localhost:5000')

ao invés de

chai.request(server)

isso corrigiu a mesma mensagem de erro que eu tinha usando Koa JS (v2) e ava js.

Skyguard
fonte
3
Depois de fazer este erro não está chegando, mas em obter dados está chegando nulo e statuscode 200 ok.
Anita Mehta
4

As respostas acima abordam corretamente o problema: supertestquer um http.Serverpara trabalhar. No entanto, chamar app.listen()para obter um servidor também iniciará um servidor de escuta, isso é uma prática ruim e desnecessária.

Você pode contornar isso usando 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));
});
Whyhankee
fonte
1

Tivemos o mesmo problema quando executamos o mocha usando ts-node em nosso projeto node + typescript sem servidor.

Nosso tsconfig.json tinha "sourceMap": true. Assim gerados, os arquivos .js e .js.map causam alguns problemas de transpilação engraçados (semelhantes a este). Quando executamos o mocha runner usando ts-node. Portanto, configurarei o sinalizador sourceMap como false e excluirei todos os arquivos .js e .js.map em nosso diretório src. Então o problema se foi.

Se você já gerou arquivos em sua pasta src, os comandos abaixo seriam realmente úteis.

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

Dilunika
fonte
0

Por precaução, se alguém usa Hapijs o problema ainda ocorre, porque não usa Express.js, portanto a função address () não existe.

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

A solução alternativa para fazer funcionar

// 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
fonte