【发布时间】:2019-10-31 20:05:27
【问题描述】:
我正在测试以下服务:
@Injectable()
export class TripService {
private readonly logger = new Logger('TripService');
constructor(
@InjectRepository(TripEntity)
private tripRepository: Repository<TripEntity>
) {}
public async showTrip(clientId: string, tripId: string): Promise<Partial<TripEntity>> {
const trip = await this.tripRepository
.createQueryBuilder('trips')
.innerJoinAndSelect('trips.driver', 'driver', 'driver.clientId = :clientId', { clientId })
.where({ id: tripId })
.select([
'trips.id',
'trips.distance',
'trips.sourceAddress',
'trips.destinationAddress',
'trips.startTime',
'trips.endTime',
'trips.createdAt'
])
.getOne();
if (!trip) {
throw new HttpException('Trip not found', HttpStatus.NOT_FOUND);
}
return trip;
}
}
我的存储库模拟:
export const repositoryMockFactory: () => MockType<Repository<any>> = jest.fn(() => ({
findOne: jest.fn(entity => entity),
findAndCount: jest.fn(entity => entity),
create: jest.fn(entity => entity),
save: jest.fn(entity => entity),
update: jest.fn(entity => entity),
delete: jest.fn(entity => entity),
createQueryBuilder: jest.fn(() => ({
delete: jest.fn().mockReturnThis(),
innerJoinAndSelect: jest.fn().mockReturnThis(),
innerJoin: jest.fn().mockReturnThis(),
from: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
execute: jest.fn().mockReturnThis(),
getOne: jest.fn().mockReturnThis(),
})),
}));
我的tripService.spec.ts:
import { Test, TestingModule } from '@nestjs/testing';
import { TripService } from './trip.service';
import { MockType } from '../mock/mock.type';
import { Repository } from 'typeorm';
import { TripEntity } from './trip.entity';
import { getRepositoryToken } from '@nestjs/typeorm';
import { repositoryMockFactory } from '../mock/repositoryMock.factory';
import { DriverEntity } from '../driver/driver.entity';
import { plainToClass } from 'class-transformer';
describe('TripService', () => {
let service: TripService;
let tripRepositoryMock: MockType<Repository<TripEntity>>;
let driverRepositoryMock: MockType<Repository<DriverEntity>>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
TripService,
{ provide: getRepositoryToken(DriverEntity), useFactory: repositoryMockFactory },
{ provide: getRepositoryToken(TripEntity), useFactory: repositoryMockFactory },
],
}).compile();
service = module.get<TripService>(TripService);
driverRepositoryMock = module.get(getRepositoryToken(DriverEntity));
tripRepositoryMock = module.get(getRepositoryToken(TripEntity));
});
it('should be defined', () => {
expect(service).toBeDefined();
expect(driverRepositoryMock).toBeDefined();
expect(tripRepositoryMock).toBeDefined();
});
describe('TripService.showTrip()', () => {
const trip: TripEntity = plainToClass(TripEntity, {
id: 'one',
distance: 123,
sourceAddress: 'one',
destinationAddress: 'one',
startTime: 'one',
endTime: 'one',
createdAt: 'one',
});
it('should show the trip is it exists', async () => {
tripRepositoryMock.createQueryBuilder.mockReturnValue(trip);
await expect(service.showTrip('one', 'one')).resolves.toEqual(trip);
});
});
});
我想模拟对tripRepository.createQueryBuilder().innerJoinAndSelect().where().select().getOne();的调用
第一个问题,我应该在这里模拟链式调用吗,因为我认为它应该已经在 Typeorm 中进行了测试。
其次,如果我想模拟传递给每个链式调用的参数,最后还要模拟返回值,我该怎么做呢?
【问题讨论】:
-
我会说最好只使用具有特定 NODE_ENV 和配置的测试数据库,以便您的测试包含所有的 sql 事务管理。在我看来,模拟 TypeOrm SQL 交互是测试服务的次优方法,而且在开发时间上也相当昂贵。
-
@zenbeni 我将在 e2e 测试中使用测试数据库进行测试。这只是一个单元测试,所以我不想使用数据库,因为它会减慢测试并增加与数据库的紧密耦合。
-
这项服务背后的意义是什么?它似乎是您的存储库的包装器。如果你想进行单元测试,你应该对你的业务逻辑而不是持久层进行单元测试。 TripRepository 是来自图书馆还是您拥有它?您的测试方法似乎更像是一种功能测试,您应该为其设置一个实际的测试数据库。
-
另外,我认为你不应该创建一个单元测试这个服务。该服务依赖于数据库。模拟整个数据库连接是没有意义的。相反,此服务是您对其他服务的依赖项,应该被嘲笑。对于其他服务,您应该编写测试:“假设我的 TripService 给了我一次旅行,那么我希望这会发生”和“假设我得到一个异常,我希望它以这种方式处理”您不应该模拟每个调用存储库。这是您的底层持久层。假设它正在工作。
-
@k0pernikus 这个服务不仅仅是一个包装器,它在查询前后都有业务逻辑。我只是举个例子。真正的问题是关于模拟链式函数调用。
标签: node.js typescript jestjs nestjs typeorm