繁体   English   中英

使用 TypeScript 在 Jest 中模拟 ES6 类方法

[英]Mocking ES6 class method in Jest with TypeScript

过去一周我一直在为此苦苦挣扎,所以我终于需要承认我不知道为什么会发生这种情况。

我正在使用带有 express 和 TypeScript 的 supertest 来测试我的一些 API 路由的实现。 请记住,我正在主动打开数据库连接并在此处写入数据库,而不是对这些方法使用模拟。

但是,我的某些控制器依赖于附加到每个控制器的记录器实例(这只是一个 ES6 类)。

我试图弄清楚为什么它没有用下面的代码正确地模拟它:

utils/logger.ts

class Logger {
  private filename: string

  public constructor(filename: string) {
    this.filename = filename
  }

  public debug(message: string) {
    console.log('debug:', message)
  }
}

controllers/user.ts

class UserController {
  public logger: any

  constructor() {
    this.logger = new Logger('UserController.ts')
  }

  async create(req: Request, res: Response) {
    this.logger.debug('creating') <-- THIS KEEPS ERRORING with: TypeError: Cannot read property 'logger' of undefined
    return res.status(200).end()
  }
}

export default new UserController()

server/routes/user.ts

import users from '../controllers/user'

class UserRoutes {
  constructor(server: any) {
    server.post('/api/v1/users/create', users.create)
  }
}

export default UserRoutes

这是我传递给supertest的应用服务器: server.ts

import bodyParser from 'body-parser'
import express from 'express'
import database from '../server/db'
import Routes from '../server/routes'

class TestApp {
  public server: any
  public database: any

  constructor() {
    this.initDatabase()
    this.run()
  }

  public async run(): Promise<void> {
    this.server = express()
    this.middleware()
    this.routes()
  }

  private middleware(): void {
    this.server.use(bodyParser.json())
  }

 private routes(): void {
    new Routes(this.server)
  }

  public initDatabase(): void {
    this.database = new database()
  }

  public closeDatabase(): void {
    this.database.close()
  }
}

export default TestApp

最后是实际测试:

import express, { Application } from 'express'
import request from 'supertest'
import TestApp from './server'
let server: any

const initServer = () => {
  jest.mock('../server/controllers/user', () => {
    return jest.fn().mockImplementation(() => {
      return {
        logger: {
          debug: jest.fn()
        }
      }
    })
  })

  const testApp = new TestApp()
  const server: Application = testApp.server
  const exp = express()
  return exp.use(server)
}

beforeAll(async () => {
  server = initServer()
})

describe('POST /api/v1/users', () => {
  test('should create a new user', async () => {
    const res = await request(server)
      .post('/api/v1/users/create')
      .send({
        username: 'testing'
      })
      expect(res.status).toEqual(200)
  })
})

我已经尝试logger.ts用户控制器中的logger实例以及实际的logger.ts整个类,但到目前为止没有任何效果。 这是完整的堆栈跟踪:

  TypeError: Cannot read property 'logger' of undefined

  46 |     
> 47 |     this.logger.debug('creating user')
     |          ^
  48 |
  49 |     try {
  50 |       const user = await User.create({

  at src/server/controllers/user.ts:47:10
  at src/server/controllers/user.ts:8:71
  at __awaiter (src/server/controllers/user.ts:4:12)
  at create (src/server/controllers/user.ts:50:16)
  at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
  at next (node_modules/express/lib/router/route.js:137:13)
  at Route.dispatch (node_modules/express/lib/router/route.js:112:3)
  at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
  at node_modules/express/lib/router/index.js:281:22
  at Function.process_params (node_modules/express/lib/router/index.js:335:12)
  at next (node_modules/express/lib/router/index.js:275:10)
  at SessionStrategy.strategy.pass (node_modules/passport/lib/middleware/authenticate.js:343:9)
  at SessionStrategy.authenticate (node_modules/passport/lib/strategies/session.js:75:10)
  at attempt (node_modules/passport/lib/middleware/authenticate.js:366:16)
  at authenticate (node_modules/passport/lib/middleware/authenticate.js:367:7)
  at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
  at trim_prefix (node_modules/express/lib/router/index.js:317:13)
  at node_modules/express/lib/router/index.js:284:7
  at Function.process_params (node_modules/express/lib/router/index.js:335:12)
  at next (node_modules/express/lib/router/index.js:275:10)

  console.log src/server/controllers/user.ts:46

为什么this (用户控制器实例)总是undefined

在这里你失去了上下文:

server/routes/user.ts

server.post('/api/v1/users/create', users.create)

有几个选项可以避免它

  1. 将方法定义为控制器中的属性
controllers/user.ts

class UserController {
  // ...
  create = async (req: Request, res: Response) => {
    this.logger.debug('creating');
    return res.status(200).end();
  }
  // ...
}
  1. 将方法绑定到实例(它创建了另一个功能)
server/routes/user.ts

class UserRoutes {
  constructor(server: any) {
    server.post('/api/v1/users/create', users.create.bind(users));
  }
}

  1. 与上一个几乎相同,但使用箭头功能而不是bind
server/routes/user.ts

class UserRoutes {
  constructor(server: any) {
    server.post('/api/v1/users/create', (req, res) => users.create(req, res));
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM