การทดสอบ Mocha API: การรับ 'TypeError: app.address ไม่ใช่ฟังก์ชัน'


105

ปัญหาของฉัน

ฉันได้เข้ารหัส CRUD ง่าย API มากและฉันเพิ่งเริ่มต้นการเขียนโปรแกรมยังทดสอบบางอย่างใช้chaiและแต่ฉันมีปัญหาเมื่อการทดสอบการทำงานของฉันด้วยchai-http$ mocha

เมื่อฉันเรียกใช้การทดสอบฉันได้รับข้อผิดพลาดต่อไปนี้บนเชลล์:

TypeError: app.address is not a function

รหัสของฉัน

นี่คือตัวอย่างการทดสอบของฉัน ( /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...

  });

});

และexpressไฟล์แอพของฉัน( /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);
});

และ ( /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;

หมายเหตุเพิ่มเติม

ฉันได้ลองออกจากระบบserverตัวแปรในไฟล์/tests/server-test.jsก่อนที่จะทำการทดสอบ:

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

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

server: {}และฉันผลของการที่เป็นวัตถุที่ว่างเปล่า

คำตอบ:


236

คุณไม่ได้ส่งออกอะไรในโมดูลแอปของคุณ ลองเพิ่มสิ่งนี้ลงในไฟล์ app.js ของคุณ:

module.exports = server

36
ปัญหาเดียวที่ฉันมีคือรหัสแอปพลิเคชันไม่ควรเปลี่ยนให้พอดีกับการทดสอบ
dman

@chovy คุณแก้ไขปัญหานี้หรือไม่? ฉันมีทางเลือกอื่นด้านล่างที่เหมาะกับฉัน
Skyguard

1
ปัญหาที่อาจเกิดขึ้นคือการจัดการกับเซิร์ฟเวอร์ที่มีบริการ async เนื่องจาก chai-http ไม่รู้ตรรกะของสิ่งนั้นและจะทำงานโดยตรงก่อนที่เซิร์ฟเวอร์ของคุณจะเริ่มทำงานอย่างสมบูรณ์
atom2ueki

1
@Nabuska ในกรณีนี้เซิร์ฟเวอร์อาจถูกตั้งค่าด้วย app.listen (... )
Garr Godfrey

สำหรับการทดสอบของคุณคุณห้ามใช้ app.listen () ซึ่งเริ่มต้นเซิร์ฟเวอร์จริงให้ใช้ http.createServer () แทน
Whyhankee

43

มันเป็นสิ่งสำคัญในการส่งออกhttp.Serverวัตถุกลับโดยapp.listen(3000)แทนเพียงฟังก์ชั่นมิฉะนั้นคุณจะได้รับappTypeError: app.address is not a function

ตัวอย่าง:

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

2
คุณควรระบุคำตอบไว้ในคำตอบว่าเหตุใดจึง "สำคัญในการส่งออกhttp.Serverวัตถุ"
GrumpyCrouton

@GrumpyCrouton ฉันเพิ่มข้อผิดพลาดที่คุณจะได้รับเป็นอย่างอื่น
Kim Kern

1
ขอบคุณคุณคิม ฉันให้ +1 กับคุณเพราะคิดว่าจะช่วยเพิ่มคำตอบของคุณ ในอนาคตคุณควรจะอธิบายว่าทำไม ลองนึกภาพว่ามีคนมองคำถามนี้และมีเพียงความรู้พื้นฐานคำตอบที่ไตร่ตรองและอธิบายอย่างดีจะสอนพวกเขาได้มากขึ้น
GrumpyCrouton

สำหรับการทดสอบของคุณคุณห้ามใช้ app.listen () ซึ่งเริ่มต้นเซิร์ฟเวอร์จริงให้ใช้ http.createServer () แทน
Whyhankee

28

สิ่งนี้อาจช่วยได้เช่นกันและเป็นไปตามจุด @dman ในการเปลี่ยนรหัสแอปพลิเคชันเพื่อให้เหมาะกับการทดสอบ

ส่งคำขอของคุณไปยัง localhost และพอร์ตตามต้องการ chai.request('http://localhost:5000')

แทน

chai.request(server)

สิ่งนี้แก้ไขข้อความแสดงข้อผิดพลาดเดียวกันกับที่ฉันใช้ Koa JS (v2) และ ava js


3
หลังจากทำข้อผิดพลาดนี้จะไม่เกิดขึ้น แต่ในการรับข้อมูลมาเป็นค่าว่างและรหัสสถานะ 200 ตกลง
Anita Mehta

5

คำตอบข้างต้นแก้ไขปัญหาได้อย่างถูกต้อง: supertestต้องการhttp.Serverให้ดำเนินการ อย่างไรก็ตามการโทรapp.listen()ไปรับเซิร์ฟเวอร์จะเป็นการเริ่มเซิร์ฟเวอร์การรับฟังซึ่งเป็นการปฏิบัติที่ไม่ดีและไม่จำเป็น

คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยใช้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));
});


1

เรามีปัญหาเดียวกันเมื่อเราเรียกใช้ mocha โดยใช้ ts-node ใน node + typescript serverless project ของเรา

tsconfig.json ของเรามี "sourceMap": true ไฟล์ที่สร้างขึ้น, .js และ. js.map ทำให้เกิดปัญหาการถ่ายโอนที่ตลก (คล้ายกับสิ่งนี้) เมื่อเราเรียกใช้ mocha runner โดยใช้ ts-node ดังนั้นฉันจะตั้งค่าแฟล็ก sourceMap เป็นเท็จและลบไฟล์. js และ. js.map ทั้งหมดในไดเรกทอรี src ของเรา จากนั้นปัญหาจะหมดไป

หากคุณสร้างไฟล์ในโฟลเดอร์ src แล้วคำสั่งด้านล่างจะเป็นประโยชน์มาก

ค้นหา src -name " .js.map" -exec rm {} \; ค้นหา src -name " .js" -exec rm {} \;


0

ในกรณีที่มีคนใช้ Hapijs ปัญหายังคงเกิดขึ้นเนื่องจากไม่ได้ใช้ Express.js ฟังก์ชัน address () จึงไม่มีอยู่

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

วิธีแก้ปัญหาเพื่อให้มันใช้งานได้

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

0

ฉันใช้ Jest และ Supertest แต่ได้รับข้อผิดพลาดเดียวกัน เป็นเพราะเซิร์ฟเวอร์ของฉันใช้เวลาในการตั้งค่า (เป็น async ในการตั้งค่า db, อ่าน config ฯลฯ ) ฉันจำเป็นต้องใช้ตัวbeforeAllช่วยของ Jest เพื่อให้การตั้งค่า async ทำงานได้ ฉันต้องรีแฟคเตอร์เซิร์ฟเวอร์ของฉันเพื่อแยกการฟังและใช้คำแนะนำของ @ Whyhankee เพื่อสร้างเซิร์ฟเวอร์ของการทดสอบแทน

index.js

export async function createServer() {
  //setup db, server,config, middleware
  return express();
}

async function startServer(){
  let app = await createServer();
  await app.listen({ port: 4000 });
  console.log("Server has started!");
}

if(process.env.NODE_ENV ==="dev") startServer();

test.ts

import {createServer as createMyAppServer} from '@index';
import { test, expect, beforeAll } from '@jest/globals'
const supertest = require("supertest");
import * as http from 'http';
let request :any;

beforeAll(async ()=>{
  request = supertest(http.createServer(await createMyAppServer()));
})

test("fetch users", async (done: any) => {
  request
    .post("/graphql")
    .send({
      query: "{ getQueryFromGqlServer (id:1) { id} }",
    })
    .set("Accept", "application/json")
    .expect("Content-Type", /json/)
    .expect(200)
    .end(function (err: any, res: any) {
      if (err) return done(err);
      expect(res.body).toBeInstanceOf(Object);
      let serverErrors = JSON.parse(res.text)['errors'];
      expect(serverErrors.length).toEqual(0);
      expect(res.body.data.id).toEqual(1);
      done();
    });
});

แก้ไข:

ฉันยังมีข้อผิดพลาดเมื่อใช้data.foreach(async()=>...ควรใช้for(let x of...ในการทดสอบของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.