Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest&supertest ApI testing returning TypeError: app.address is not a function

I am currently making an API with typescript, node, express and testing with jest and supertest. I had no problem when I was using Javascript, but I recently changed my project file from JS to TS including test files, and when I start testing, I get the error below in all my test suites on supertest request part and this is one of my test suites on my terminal when I start test.

  TypeError: app.address is not a function

  37 |     it("should return 400 if email is invalid", async () => {
  38 |       const res = await request(server)
> 39 |         .post("/api/users/auth/register")
     |          ^
  40 |         .send({
  41 |           email: "nomail",
  42 |           password: "validpassword123",

This is my test files auth.test.ts:

import * as request from 'supertest';
import { User } from '../../../../src/models/User';
import * as mongoose from 'mongoose';
import getKeys from '../../../../src/config/keys';

describe("/api/users/auth", () => {
  let server;
  let accessToken = "Bearer accessToken";
  let email;
  let password;

  beforeAll(async () => {
    server = import('../../../../src/index')
    await mongoose.connect(getKeys().mongoURI);
  })

  afterAll(async () => {
    await server.close();
    await mongoose.disconnect();
  })

  it("should return 400 if email is invalid", async () => {
    const res = await request(server)
      .post("/api/users/auth/register")
      .send({
        email: "nomail",
        password: "validpassword123",
        name: "name"
      });

    expect(res.status).toBe(400);
    expect(res.body).toHaveProperty('errArray')
  });
}

and This is my src/index.ts file, which is Entry point.

import * as express from 'express';
import * as passport from 'passport';
import * as bodyParser from 'body-parser';
import * as path from 'path';
import * as session from 'express-session';
import getKeys from './config/keys';

const port = 3001 || process.env.PORT;
const server = app.listen(port, () =>
  console.log(`Server running on port ${port}`)
);


export default server;

I've tried changing export and importing server syntax to all commonjs syntax and install and set all dependencies relevant to this including @types/supertest, @types/jest, ts-jest , here is my settings jest.config.js

module.exports = {
  verbose: true,
  testURL: 'http://localhost',
  testEnvironment: "node",
  roots: [
    "<rootDir>"
  ],
  transform: {
    "^.+\\.tsx?$": "ts-jest"
  },
  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
  globals: {
    "ts-jest": {
      "tsConfigFile": "tsconfig.json"
    }
  },
  moduleFileExtensions: [
    "ts",
    "tsx",
    "js",
    "jsx",
    "json",
    "node"
  ],

}

tsconfig.json

 {
  "compilerOptions": {
    "outDir": "./dist",
    "moduleResolution": "node",
    "sourceMap": true,
    "module": "commonjs",
    "allowJs": true,
    "target": "es5",
    "noUnusedParameters": false,
    "allowUnreachableCode": true,
    "allowUnusedLabels": true,
    "types": [
      "jest",
      "node",
      "express",
      "mongoose",
      "body-parser",
      "supertest"
    ],
    "lib": [
      "es2015"
    ]
  },
  "include": [
    "./src/**/*",
    "index.ts"
  ],
  "exclude": [
    "./node_modules",
    "**/*.spec.ts",
    "**/*.test.ts"
  ]
}

package.json

    "scripts": {
    "test": "jest --watchAll --runInBand",
    "coverage": "jest --coverage",
    "start": "ts-node src/index.ts",
    "server": "./node_modules/nodemon/bin/nodemon.js",
    "client": "npm start --prefix ../client",
    "dev": "concurrently \"npm run server\" \"npm run client\""
  },
"devDependencies": {
"@types/body-parser": "^1.17.0",
"@types/express": "^4.16.0",
"@types/jest": "^23.3.12",
"@types/mongoose": "^5.3.7",
"@types/node": "^10.12.18",
"@types/supertest": "^2.0.7",
"jest": "^23.6.0",
"nodemon": "^1.18.9",
"supertest": "^3.3.0",
"ts-jest": "^23.10.5",
"ts-node": "^7.0.1",
"typescript": "^3.2.2"
}
like image 658
Riaco Avatar asked Jan 17 '19 07:01

Riaco


People also ask

Why is Jest used for?

Jest is a JavaScript testing framework designed to ensure correctness of any JavaScript codebase. It allows you to write tests with an approachable, familiar and feature-rich API that gives you results quickly. Jest is well-documented, requires little configuration and can be extended to match your requirements.

What is describe Jest?

Our first friend is describe , a Jest method for containing one or more related tests. Every time you start writing a new suite of tests for a functionality wrap it in a describe block. As you can see it takes two arguments: a string for describing the test suite, and a callback function for wrapping the actual test.

Why is Jest so popular?

Jest makes mocking easy by using a custom resolver for imports and enables you to use the mocked imports with mock function APIs to spy on function calls. The mock function comes in handy especially in unit testing as they let you test the function logic without having to worry about its dependencies.

What is difference between Jest and enzyme?

Both Jest and Enzyme are meant to test the react applications. Jest can be used with any other Javascript framework, but Enzyme is meant to run on react only. Jest can be used without Enzyme, and snapshots can be created and tested perfectly fine. But the Enzyme adds additional functionality to it.


1 Answers

The reason is your server passed into supertest is undefined. supertest will use app.address() internally, see this line. That's why it throw an error:

TypeError: app.address is not a function

If you want to import the server dynamically, You should change:

let server;
beforeAll(async () => {
  server = import('../../../../src/index')
})

to:

let server;
beforeAll(async () => {
  const mod = await import('../../../../src/index');
  server = (mod as any).default;
});

E.g.

index.ts:

import express from 'express';

const app = express();

app.post('/api/users/auth/register', (req, res) => {
  res.status(400).json({ errArray: [] });
});

const port = 3001 || process.env.PORT;
const server = app.listen(port, () => console.log(`Server running on port ${port}`));

export default server;

index.test.ts:

import request from 'supertest';

describe('/api/users/auth', () => {
  let server;
  beforeAll(async () => {
    const mod = await import('./');
    server = (mod as any).default;
  });

  afterAll((done) => {
    if (server) {
      server.close(done);
    }
  });

  it('should return 400 if email is invalid', async () => {
    const res = await request(server)
      .post('/api/users/auth/register')
      .send({
        email: 'nomail',
        password: 'validpassword123',
        name: 'name',
      });

    expect(res.status).toBe(400);
    expect(res.body).toHaveProperty('errArray');
  });
});

Integration test result with coverage report:

☁  jest-codelab [master] ⚡  npx jest --coverage --verbose /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/54230886/index.test.ts
 PASS  src/stackoverflow/54230886/index.test.ts (10.306s)
  /api/users/auth
    ✓ should return 400 if email is invalid (56ms)

  console.log src/stackoverflow/54230886/index.ts:437
    Server running on port 3001

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |       50 |      100 |      100 |                   |
 index.ts |      100 |       50 |      100 |      100 |                 9 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        11.875s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/54230886

like image 155
slideshowp2 Avatar answered Sep 23 '22 04:09

slideshowp2