Mocha API Testing: getting 'TypeError: app.address is not a function'

87,825

Solution 1

You don't export anything in your app module. Try adding this to your app.js file:

module.exports = server

Solution 2

It's important to export the http.Server object returned by app.listen(3000) instead of just the function app, otherwise you will get TypeError: app.address is not a function.

Example:

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 ...', () => {

Solution 3

This may also help, and satisfies @dman point of changing application code to fit a test.

make your request to the localhost and port as needed chai.request('http://localhost:5000')

instead of

chai.request(server)

this fixed the same error message I had using Koa JS (v2) and ava js.

Solution 4

The answers above correctly address the issue: supertest wants an http.Server to work on. However, calling app.listen() to get a server will also start a listening server, this is bad practice and unnecessary.

You can get around by this by using 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));
});

Solution 5

Just in case, if someone uses Hapijs the issue still occurs, because it does not use Express.js, thus address() function does not exist.

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

The workaround to make it work

// 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()
      })
    })
  })
Share:
87,825
charliebrownie
Author by

charliebrownie

I am a Software Engineer with strong interest in: Clean Code and good practices the whole Software Development Process Web Development & Technologies "Life is about creating yourself." "Do what you love, love what you do."

Updated on November 20, 2020

Comments

  • charliebrownie
    charliebrownie over 3 years

    My Issue

    I've coded a very simple CRUD API and I've started recently coding also some tests using chai and chai-http but I'm having an issue when running my tests with $ mocha.

    When I run the tests I get the following error on the shell:

    TypeError: app.address is not a function

    My Code

    Here is a sample of one of my tests (/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...
    
      });
    
    });
    

    And my express app files (/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);
    });
    

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

    Extra notes

    I've tried logging out the server variable in the /tests/server-test.js file before running the tests:

    ...
    var server = require('../server/app'); // my express app
    ...
    
    console.log('server: ', server);
    ...
    

    and I the result of that is an empty object: server: {}.

  • dman
    dman about 8 years
    the only issue I have with this is that the application code should never be changed to fit a test.
  • Skyguard
    Skyguard about 7 years
    @chovy did you resolve this? I have an alternative below that worked for me.
  • atom2ueki
    atom2ueki about 7 years
    the potential problem is dealing with server which contains a async services, because chai-http don't know the logic of that, and it will run directly even before your server fully started
  • Anita Mehta
    Anita Mehta almost 7 years
    After doing this error is not coming, but in get data is coming null and statuscode 200 ok.
  • GrumpyCrouton
    GrumpyCrouton almost 7 years
    You should include in your answer why it's "important to export the http.Server object".
  • Kim Kern
    Kim Kern almost 7 years
    @GrumpyCrouton I added the error you will get otherwise
  • GrumpyCrouton
    GrumpyCrouton almost 7 years
    Thank you Kim. I gave you a +1 because I think that does improve your answer. In the future, you should explain why. Imagine someone looking at this question and only has basic knowledge, a well thought out and explained answer will teach them a lot more.
  • Garr Godfrey
    Garr Godfrey about 6 years
    @Nabuska in this case server is probably already set with app.listen(...)
  • Whyhankee
    Whyhankee almost 5 years
    For your tests you shoud not use app.listen() which starts an actual server, use http.createServer() instead
  • Whyhankee
    Whyhankee almost 5 years
    For your tests you shoud not use app.listen() which starts an actual server, use http.createServer() instead
  • Amirhosein Al
    Amirhosein Al over 4 years
    Works for me but when I wrap it in an if(!process.env.prod) it doesn't
  • 1011 1110
    1011 1110 about 3 years
    Honestly I think this should be the accepted answer, calling app.listen() in the app will make you run into issues later.
  • Catfish
    Catfish over 2 years
    The problem with this answer is that you create a separate app to run your tests, which doesn't include the actual code of your route handlers. How I've always solved this problem is simply have a function which accepts a param on whether to call app.listen or not and that function always returns the app object.
  • an1waukoni
    an1waukoni about 2 years
    Thanks for your answer and explanation. I've been debugging this for two days straight!