I am building a nestJS app with typeOrm. It is a property management application.
When a tenant pays a landlord, it creates a RentPayment
entity, which triggers the creation or update of a Rent
entity. This latter will have a list of RentPayments
for a particular tenant. A RentPaymentReceipt
will be generated when the sum of the rent payments matches the total rent.
I am currently developing the CreateRentService
. I have injected the repositories I need :
@Injectable()
export class CreateRentService {
constructor(
@InjectRepository(Rent)
private rentRepository: Repository<Rent>,
@InjectRepository(RentPayment)
private rentPaymentRepository: Repository<RentPayment>,
@InjectRepository(Tenant)
private tenantRepository: Repository<Tenant>,
) {}
async execute(rentPaymentId: number) {
const rentPayment = await this.rentPaymentRepository.findOneOrFail({
where: {
id: rentPaymentId,
},
});
const tenant = rentPayment.tenant;
const rent = new Rent(
tenant.userId,
tenant,
rentPayment.paymentDate.toISOString(),
[rentPayment],
);
return this.rentRepository.save(rent);
}
When I run the service via an integration test, I have a findOneOrFail is not a function
error:
● Create rent › should create a rent when it does not already exist
TypeError: this.rentPaymentRepository.findOneOrFail is not a function
18 |
19 | async execute(rentPaymentId: number) {
> 20 | const rentPayment = await this.rentPaymentRepository.findOneOrFail({
| ^
21 | where: {
22 | id: rentPaymentId,
23 | },
My property module imports all the typeOrm entities required:
property.module.ts:
@Module({
imports: [TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent])],
providers: [
RentPaymentService,
CreateRentService,
]
});
I have called the same function(findOneOrFail
) from within the test and there is no problem. Here is my test file:
describe('Create rent', () => {
let typeOrmRentPaymentRepository: Repository<RentPayment>;
let typeOrmTenantRepository: Repository<Tenant>;
let typeOrmPropertyRepository: Repository<Property>;
let typeOrmRentRepository: Repository<Rent>;
let createRentService: CreateRentService;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot(postgresqlTestDatasource.options),
TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent]),
],
providers: [
{
provide: getRepositoryToken(RentPayment),
useValue: Repository,
},
{
provide: getRepositoryToken(Tenant),
useValue: Repository,
},
{
provide: getRepositoryToken(Property),
useValue: Repository,
},
{
provide: getRepositoryToken(Rent),
useValue: Repository,
},
CreateRentService,
],
}).compile();
typeOrmRentPaymentRepository = module.get<Repository<RentPayment>>(
getRepositoryToken(RentPayment),
);
typeOrmTenantRepository = module.get<Repository<Tenant>>(
getRepositoryToken(Tenant),
);
typeOrmPropertyRepository = module.get<Repository<Property>>(
getRepositoryToken(Property),
);
typeOrmRentRepository = module.get<Repository<Rent>>(
getRepositoryToken(Rent),
);
createRentService = module.get<CreateRentService>(CreateRentService);
});
afterEach(async () => {
await typeOrmRentRepository.clear();
await typeOrmRentPaymentRepository.clear();
await typeOrmTenantRepository.clear();
await typeOrmPropertyRepository.clear();
});
it('should create a rent when it does not already exist', async () => {
// Given
const userId = 32;
const property = await typeOrmPropertyRepository.save(new Property(userId));
const tenant = await typeOrmTenantRepository.save(
new Tenant(userId, property),
);
const rentPayment = await typeOrmRentPaymentRepository.save(
new RentPayment(tenant, 170000, new Date(2023, 0, 2, 7)),
);
// When
await createRentService.execute(rentPayment.id);
// Then
const expectedRent = new Rent(userId, tenant, '2023-01-01', [rentPayment]);
const createdRents = await typeOrmRentRepository.find();
expect(createdRents.length).toEqual(1);
expect(createdRents[0].tenant).toEqual(expectedRent.tenant);
expect(createdRents[0].date).toEqual(expectedRent.date);
expect(createdRents[0].rentPayments.length).toEqual(1);
expect(createdRents[0].rentPayments[0]).toEqual(rentPayment);
});
});
I am expecting the function to be part of the repository, as written in typeOrm documentation, and as my ide (IntelliJ + webstorm plugin) is suggesting me.
It is the same behavior with the tenantRepository
...
I have spent several hours reading typeOrm and nestJs documentation, as well as trying to find an answer on SO. The same type of problem I have seen were because the entity was not imported in the module via
imports: [TypeOrmModule.forFeature([entity])
But I think I have done it as suggested and required by the documentation.
Many thanks in advance for your help on this.
@Jay's answer set me on the track to find my error. As I just wanted to obtain a reference to the "real" injected repository, I just had to get rid of the different repository providers I gave to my test config:
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot(postgresqlTestDatasource.options),
TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent]),
],
providers: [
CreateRentService,
],
}).compile();
typeOrmRentPaymentRepository = module.get<Repository<RentPayment>>(
getRepositoryToken(RentPayment),
);
typeOrmTenantRepository = module.get<Repository<Tenant>>(
getRepositoryToken(Tenant),
);
typeOrmPropertyRepository = module.get<Repository<Property>>(
getRepositoryToken(Property),
);
typeOrmRentRepository = module.get<Repository<Rent>>(
getRepositoryToken(Rent),
);
createRentService = module.get<CreateRentService>(CreateRentService);
});
The module.get<Repository<Entity>>(getRepositoryToken(Entity)
lines are doing the job of getting the correct reference to the injected repositories