lundi 28 septembre 2020

How can I use jest leak detector correctly?

I wrote my test code named test.test.ts. This require testUtils.ts as client and call createClient() and removeClient().

test('test', async () => {
  let client = require('../testUtils')
  expect(1).toBe(1)
  jest.resetModules()
  await client.createClient()
  await client.removeClient()
  let detector = new LeakDetector(client)
  console.log(await detector.isLeaking())
  client = null
  jest.resetModules()
  console.log(await detector.isLeaking())
  global.gc()
})

And here is testUtils.ts. This file require and use some modules. After all job I call removeClient() in test.test.ts file. So I expect all of modules must be garbage collected. But they didn't...

import LeakDetector from 'jest-leak-detector'
export let testClient = null
export let query = null
export let mutate = null

let server = null
let {ApolloServer} = require('apollo-server-express')
let {createTestClient} = require('apollo-server-testing')
let jwt = require('jsonwebtoken')
let {Config} = require('./config')
let {db} = require('my sequelize model')
let {basicDefs, mutationDefs} = require('./defs')
let {AuthDirective} = require('./directives')
let {resolvers} = require('./resolvers')

export const generateToken = async () => {
  const user = await db.BlahBlah.findOne({
    attributes: ['BlahBlah1', 'BlahBlah2'],
    where: {
      username: 'BlahBlah',
    },
  })
  return jwt.sign({BlahBlah: BlahBlah, BlahBlah: BlahBlah2}, BlahBlah3)
}

export const createClient = async () => {
  if (!testClient) {
    server = new ApolloServer({
      typeDefs: [basicDefs, mutationDefs],
      resolvers,
      schemaDirectives: {
        auth: AuthDirective,
      },
      engine: false,
      context: {
        req: {
          headers: {
            authorization: `BlahBlah ${await generateToken()}`,
          },
        },
      },
      formatError: (err) => {
        if (err.message.startsWith('jwt must be provided')) {
          return new Error('no login information')
        } else return new Error(err.message)
      },
    })
    testClient = createTestClient(server)
    query = testClient.query
    mutate = testClient.mutate
  }
}

export const removeClient = async () => {
  jest.resetModules()
  await server.stop()

  let detector = new LeakDetector(testClient)
  console.log(await detector.isLeaking())
  testClient = null
  global.gc()
  console.log(await detector.isLeaking())

  detector = new LeakDetector(mutate)
  console.log(await detector.isLeaking())
  mutate = null
  global.gc()
  console.log(await detector.isLeaking())

  detector = new LeakDetector(query)
  console.log(await detector.isLeaking())
  query = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(ApolloServer)
  console.log(await detector.isLeaking())
  ApolloServer = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(server)
  console.log(await detector.isLeaking())
  server = null
  global.gc()
  console.log(await detector.isLeaking())

  detector = new LeakDetector(createTestClient)
  console.log(await detector.isLeaking())
  createTestClient = null
  global.gc()
  console.log(await detector.isLeaking())

  detector = new LeakDetector(jwt)
  console.log(await detector.isLeaking())
  jwt = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(Config)
  console.log(await detector.isLeaking())
  Config = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(db)
  console.log(await detector.isLeaking())
  db = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(basicDefs)
  console.log(await detector.isLeaking())
  basicDefs = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(mutationDefs)
  console.log(await detector.isLeaking())
  mutationDefs = null
  global.gc()
  console.log(await detector.isLeaking()) // here

  detector = new LeakDetector(AuthDirective)
  console.log(await detector.isLeaking())
  AuthDirective = null
  global.gc()
  console.log(await detector.isLeaking())

  detector = new LeakDetector(resolvers)
  console.log(await detector.isLeaking())
  resolvers = null
  global.gc()
  console.log(await detector.isLeaking())
}

I expected each second console.log print false because I removed references by assigning null and requested garbage collection, but some console.log(marked // here) print true.

Plus, I got this error when I ran node --expose-gc ./node_modules/.bin/jest --logHeapUsage --detectLeaks -- test.test.ts

EXPERIMENTAL FEATURE!
Your test suite is leaking memory. Please ensure all references are cleaned.

There is a number of things that can leak memory:
  - Async operations that have not finished (e.g. fs.readFile).
  - Timers not properly mocked (e.g. setInterval, setTimeout).
  - Keeping references to the global scope.

  at onResult (node_modules/@jest/core/build/TestScheduler.js:190:18)
  at node_modules/@jest/core/build/TestScheduler.js:304:17
  at node_modules/emittery/index.js:260:13
      at Array.map (<anonymous>)
  at Emittery.Typed.emit (node_modules/emittery/index.js:258:23)

Is there anyone who can explain this and know how solve this problem?

Thank you for your time.

Aucun commentaire:

Enregistrer un commentaire