Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jest not exiting due to Redis hanging after compilation error with createTestingModule #340

Open
chowed opened this issue Aug 25, 2022 · 6 comments
Labels
help wanted Extra attention is needed

Comments

@chowed
Copy link

chowed commented Aug 25, 2022

Hi @liaoliaots , thank you for all your hard work for this library

Expected Behavior

For a Jest test, Redis should close after test module (@nestjs/testing) encounters error during compilation.

Current Behavior

Redis hangs after encountering error during test module compilation. testingModule is undefined when error is encountered

   TypeError: Cannot read properties of undefined (reading 'close')

      147 |
      148 |   afterAll(async () => {
    > 149 |     await testingModule.close();
          |                         ^
      150 |   });
Jest did not exit one second after the test run has completed.

This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.

When there's no error, it is closed properly via afterAll() and everything is fine.

Possible Solution

Saw a similar issue raised but its solution (setting closeClient to true) did not work

Steps to Reproduce (for bugs)

testingModule = await Test.createTestingModule({
  imports: [
    RedisModule.forRoot({
      config: {
        url: `redis://localhost:6379`,
      },
    }),
    TypeOrmModule.forFeature([
      <missing required entity import>
    ])
  ]
}).compile();

afterAll(async () => {
  await testingModule.close();
});

Create testing module via createTestingModule (@nestjs/testing) and leave out required entity import

Context

Trying to ensure Jest exits properly after test run completion

Your Environment

  • "@liaoliaots/nestjs-redis": "^8.2.1"
  • "@nestjs/testing": "^8.4.6"
  • Node.js version: 16.16.0
  • Operating System: macOS Monterey
@liaoliaots liaoliaots added the help wanted Extra attention is needed label Aug 30, 2022
@liaoliaots
Copy link
Owner

liaoliaots commented Aug 30, 2022

You can use the createNestApplication() method to instantiate a full Nest runtime environment(e2e testing) to try it again.
The close() method will not be called for unit testing created by createTestingModule(), therefore you should terminate redis connection manually, or use a mocked redis instance(best practice).

unit testing: https://docs.nestjs.com/fundamentals/testing#testing-utilities
e2e testing: https://docs.nestjs.com/fundamentals/testing#end-to-end-testing

@liaoliaots liaoliaots added question Further information is requested help wanted Extra attention is needed and removed help wanted Extra attention is needed question Further information is requested labels Aug 30, 2022
@richard-viney
Copy link

@liaoliaots Thanks for those details, how could the Redis connection be closed manually if we did want to test against the real thing instead of mocking?

@liaoliaots
Copy link
Owner

liaoliaots commented Sep 7, 2022

Below is a simple example:

// `RedisModule` configurations
import { Module } from '@nestjs/common';
import { RedisModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    RedisModule.forRoot({
      closeClient: false, // disable the built-in automatic close
      readyLog: true,
      config: {
        host: 'localhost',
        port: 6379,
        password: 'authpassword'
      }
    })
  ]
})
export class AppModule {}
// e2e testing, with `fastify`
describe('description', () => {
  let app: NestFastifyApplication;
  let client: Redis;

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [AppModule]
    }).compile();
    client = module.get<Redis>(getRedisToken(DEFAULT_REDIS_NAMESPACE));
    app = module.createNestApplication<NestFastifyApplication>(new FastifyAdapter());
    await app.init();
    await (app.getHttpAdapter().getInstance() as FastifyInstance).ready();
  });

  afterAll(async () => {
    await client.quit(); // close manually
    await app.close();
  });
});

@mridang
Copy link

mridang commented Mar 1, 2023

Hi @liaoliaots, I'm still unable to get this to work. When the tests close, I get this error:

    Nest could not find Symbol() element (this provider does not exist in the current context)

      36 |   afterAll(async () => {
      37 |     await redis.quit()
    > 38 |     await app?.close()
         |     ^
      39 |   })
      40 | })
      41 |

      at InstanceLinksHost.get (node_modules/@nestjs/core/injector/instance-links-host.js:24:19)
      at ModuleRef.find (node_modules/@nestjs/core/injector/abstract-instance-resolver.js:8:60)
      at ModuleRef.get (node_modules/@nestjs/core/injector/module.js:398:29)
      at RedisModule.onApplicationShutdown (node_modules/@liaoliaots/nestjs-redis/dist/redis/redis.module.js:68:48)
      at callAppShutdownHook (node_modules/@nestjs/core/hooks/on-app-shutdown.hook.js:51:35)
      at async Proxy.callShutdownHook (node_modules/@nestjs/core/nest-application-context.js:254:13)
      at async Proxy.close (node_modules/@nestjs/core/nest-application-context.js:114:9)
      at async Object.<anonymous> (test/health.e2e.spec.ts:38:5)

I've followed your instructions and this is how my tests are set up.

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule]
    }).compile()

    app = moduleRef.createNestApplication()
    await app.init()

    request = supertest(app.getHttpServer())

    redis = moduleRef.get<Redis>(getRedisToken(DEFAULT_REDIS_NAMESPACE))
  })

My AppModule is imports the Redis module like so

    RedisModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      /* eslint-disable-next-line */
      useFactory: async (config: ConfigService) => ({
        closeClient: false,
        config: {
          namespace: DEFAULT_REDIS_NAMESPACE,
          host: config.get('redis.host'),
          port: config.get('redis.port'),
          user: config.get('redis.user'),
          password: config.get('redis.pass')
        }
      } as RedisModuleOptions)
    } as RedisModuleAsyncOptions),

I'm really lost with this. Any help is appreciated?

On a side node, would you be able to explain if there is a reason why the module doesn't tear down the Redis automatically? I would be happy to contribute a pull-request if needed.

@jkasztur
Copy link

jkasztur commented Jan 5, 2024

hi @mridang, did you find a workaround for this? Currently facing this issue too..
Error points to https://github.com/liaoliaots/nestjs-redis/blob/main/packages/redis/lib/redis/redis.module.ts#L75
so it should not matter if closeClient is set to true or false, it doesn't even resolve the options

@DeepaPrasanna
Copy link

Hi @jkasztur , @mridang , were you able to solve this? I am too facing this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

6 participants