1

I am writing end-to-end tests in NestJs and using the "overrideProvider" function to setup test versions of various services (e.g. services that require database connections). I noticed though that even when I do this, the original implementation that injects the real database is still instantiated.

Is there a way to tell Nest to not create transitive dependencies that are overridden?

For example, I have a test that starts like:

...

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [ServiceModule],
    })
    // Works if I uncomment these lines:
    // .overrideProvider('Database')
    // .useValue(new TestDatabase())
    .overrideProvider('ServiceUsingDatabase')
    .useValue(new TestService())
    .compile();

...

Where the module setup is like:

import { Inject, Injectable, Module } from '@nestjs/common';

interface Database {} 

@Injectable()
class ProductionDatabase implements Database {
  constructor() {
    throw('Cannot create a production database.');
  }
}

@Injectable()
export class TestDatabase implements Database {
  constructor() {
    console.log('Creating the test database.');
  }
}

@Module({
  providers: [
    {
      provide: 'Database',
      useClass: ProductionDatabase

    }
  ],
  exports: ['Database']
})
class DatabaseModule {}


interface Service {}

@Injectable()
class ProductionService implements Service {
  constructor(@Inject('Database') private readonly database: Database) {}
}

@Injectable()
export class TestService implements Service {
  // Test implementation of the service does not Inject anything.
}

@Module({
  imports: [DatabaseModule],
  providers: [
    {
      provide: 'ServiceUsingDatabase',
      useClass: ProductionService
    }
  ],
})
export class ServiceModule {}

But, the DI system is still seeming to try and instantiate ProductionDatabase. If I explicitly override the provider for the 'Database' it works, but I'd like to avoid having to explicitly list all transitive dependencies as such.

okhobb
  • 758
  • 8
  • 22
  • Can you show the `DatabaseModule` as well? – Jay McDoniel May 19 '22 at 01:00
  • This was a good hint thanks! The DatabaseModule had useValue(). I changed that to useFactory() and got the behavior I was hoping for. – okhobb May 19 '22 at 02:17
  • This got me over one part of my question, but what I was really hoping to do was slightly different / more complicated. I'll update the question. – okhobb May 19 '22 at 03:55
  • Ah, I see, no, there's no way to have the `'Database'` dependency automatically not resolved if you override the `ServiceUsingDatabase`provider. I'll need to double check, but I believe Nest creates instances of each declared dependency as soon as it reads it and is able to – Jay McDoniel May 19 '22 at 04:21
  • I see thanks. That is how it is seeming to me as well. – okhobb May 19 '22 at 04:44

1 Answers1

1

I ended up deciding to make a "Test" Module for every Module e.g.:

import { Inject, Injectable, Module } from '@nestjs/common';

interface Database {} 

@Injectable()
class ProductionDatabase implements Database {
}

@Injectable()
export class TestDatabase implements Database {
}

@Module({
  providers: [
    {
      provide: 'Database',
      useClass: ProductionDatabase

    }
  ],
  exports: ['Database']
})
class DatabaseModule {}

@Module({
  providers: [
    {
      provide: 'Database',
      useClass: TestDatabase

    }
  ],
  exports: ['Database']
})
class TestDatabaseModule {}


interface Service {}

@Injectable()
class ProductionService implements Service {
  constructor(@Inject('Database') private readonly database: Database) {}
}

@Injectable()
export class TestService implements Service {
}

@Module({
  imports: [DatabaseModule],
  providers: [
    {
      provide: 'ServiceUsingDatabase',
      useClass: ProductionService
    }
  ],
})
export class ServiceModule {}


@Module({
  providers: [
    {
      provide: 'ServiceUsingDatabase',
      useClass: TestService
    }
  ],
})
export class TestServiceModule {}

etc... Though it turned out after some refactorings that the "Test" module wasn't needed as some Modules became pure business logic.

okhobb
  • 758
  • 8
  • 22