How to override provider for tests in NestJs and not create transitive dependencies that are not needed?

1.8k Views Asked by At

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.

1

There are 1 best solutions below

0
On BEST ANSWER

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.