z0mbieKale
z0mbieKale

Reputation: 1028

How to properly unit test a class with Mikro ORM and NestJS?

I have a service called PackageCalculator

@Injectable()
export class PackageCalculator {
  private readonly logger = new Logger(PackageCalculator.name);

  private doesPackagesMatch(packageDosages: number[]) {
    if (new Set(packageDosages).size !== 1) {
      throw new InconsistentPackageDosagesException();
    }
  }
  async calculatePackagesAmount(data: {
    medication: Medication;
    dosageId: string;
    regimenId?: string;
    customRegimen?: string;
    numberOfCycles: number;
    medicationPackage: {
      packageId: string;
      count: number;
    };
    remsProgram: RemsProgram;
    remsProgramPatientType: string;
  }) {
    // Logic here

    if (!isValidPackagesAmount) {
      throw new PackageQuantityConflictException({
        isValidPackagesAmount,
        packagesNeeded: totalPackagesNeeded,
        packagesProvided: data.medicationPackage.count,
        dosageNeeded,
        totalDosageOfPackages,
      });
    }

    return {
      isValidPackagesAmount,
      packagesNeeded: totalPackagesNeeded,
      packagesProvided: data.medicationPackage.count,
      dosageNeeded,
      totalDosageOfPackages: totalDosageOfPackages,
    };
  }
}

and this is my test file

class MockMedication extends Medication {
  override id = new UniqueEntityId("med1");
  override getRegimen = jest.fn().mockReturnValue({
    getRegimenDurationInDays: jest.fn().mockReturnValue(14),
    calculatePillsPerRegimen: jest.fn().mockReturnValue(2),
  });
  override getDosage = jest.fn().mockReturnValue({ dosage: "5" });
  override getPackage = jest
    .fn()
    .mockReturnValueOnce({ dosage: "5", pillsPerPackage: 10 })
    .mockReturnValueOnce({ dosage: "10", pillsPerPackage: 10 });
}

class MockRemsProgram extends RemsProgram {
  override id = new UniqueEntityId("remsProgram1");
  override fertilityWeekLimit = 4;
}

describe("PackageCalculator", () => {
  let packageCalculator: PackageCalculator;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [PackageCalculator],
    }).compile();

    packageCalculator = module.get(PackageCalculator);
  });

  it("should calculate the correct packages amount", async () => {
    const medication = new MockMedication({
      atcCode: new AtcCode({ value: "l04ax04" }),
      name: "lenalidomide",
    });
    const remsProgram = new MockRemsProgram({
      fertilityWeekLimit: 4,
      maxDosage: "20",
      maxDosageUnit: "mg",
      countryId: "country1",
      hasFertilityRestriction: true,
      medicationId: medication.id.toString(),
    });

    const data = {
      medication,
      dosageId: "dos1",
      regimenId: "reg1",
      numberOfCycles: 2,
      medicationPackage: { packageId: "package1", count: 1 },
      remsProgram,
      remsProgramPatientType: "type1",
    };

    const result = await packageCalculator.calculatePackagesAmount(data);

    expect(result).toEqual({
      isValidPackagesAmount: true,
      packagesNeeded: 1,
      packagesProvided: 1,
      dosageNeeded: 20,
      totalDosageOfPackages: 50,
    });
  });
});

The application has a a lot of entities and they are all loaded into the MikroORM by using autoload entities option. Each entity is in it's own feature module.

For each unit test does one need to use MikroORM.init with all of the entities provided in the entities array? I tried improting my DatabaseModule as well as the ModelsModule (this is re exporting every "feature" entity modules) to the Test module, but this is failing as well with the following error

Error: Nest can't resolve dependencies of the Symbol(mikro-orm-module-options) (?). Please make sure that the argument ConfigService at index [0] is available in the MikroOrmCoreModule context.

Potential solutions:
- Is MikroOrmCoreModule a valid NestJS module?
- If ConfigService is a provider, is it part of the current MikroOrmCoreModule?
- If ConfigService is exported from a separate @Module, is that module imported within MikroOrmCoreModule?
  @Module({
    imports: [ /* the Module containing ConfigService */ ]
  }) 

This is the DatabaseModule

@Module({
  imports: [
    CoreModule,
    ConfigModule.forFeature(DatabaseConfig),
    MikroOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => {
        const dbConfig = configService.get(
          "database.mysql"
        ) as IDatabaseConfig["mysql"];

        return {
          user: dbConfig.username,
          password: dbConfig.password,
          dbName: dbConfig.database,
          port: Number(dbConfig.port),
          driver: MySqlDriver,
          autoLoadEntities: true,
          metadataProvider: ReflectMetadataProvider,
          extensions: [SoftDeletableHandlerSubscriber],
          registerRequestContext: true
        };
      },
    }),
  ],
  providers: [GlobalSubscriber, SoftDeletableHandlerSubscriber],
})
export class DatabaseModule {}

Is there anyway to achieve unit testing like this or should all of the testing that require entities be done as integration tests?

Upvotes: 0

Views: 91

Answers (0)

Related Questions