CI등의 테스트 환경에서 PostgreSQL를 설치하지 않고 테스트하고 싶은 경우가 생길 수 있다
이때 pg-mem을 사용하면 PostgreSQL을 설치하지 않고 TypeORM 사용이 가능하다
먼저 pg-mem을 설치한다 npm i pg-mem --save
TypeOrmModule은 dataSourceFactory라는 기능을 제공하고 있다 해당 기능을 이용해서 pg-mem에서 생성한 datasoruce를 넘겨주도록 하자
TypeOrmModule.forRootAsync({
useFactory: () => {
return {
type: 'postgres',
host: 'your-host',
database: 'your-database-name',
username: 'your-username',
password: 'your-password',
entities: ['your-entity-file-path or entity class'],
};
},
// useFactory에서 반환된 options값이 dataSourceFactory의 매개변수로 전달됩니다.
dataSourceFactory: (options) => {
const memoryDb = newDb();
const dataSoruce = memoryDb.adapters.createTypeormDataSource({
...options,
// test 환경에 테이블이 자동으로 생성되도록 Synchronize를 true로 설정합니다.
synchronize: true,
});
return dataSoruce;
},
})
이 상태에서 서버를 실행하면 아래와 같은 에러가 호출된다
Failed SQL statement: SELECT version();;
SELECT version();
이라는 쿼리를 TypeORM에서 실행하고 있는데 pg-mem에서 지원하지 않기 때문이다
TypeORM에서 왜 버전을 체크하는지 확인 해보니 isGeneratedColumnsSupported 를 처리하기 위함으로 보인다
현재 사용하고있는 PostgreSQL 버전에 맞춰 pg-mem에 추가 설정을 아래와 같이 하자
memoryDb.public.registerFunction({
name: 'version',
implementation: () => 'PostgreSQL 16.1', // 버전 확인
});
다시 서버를 실행하면 에러가 또 발생하게 된다
Failed SQL statement: SELECT * FROM current_database();
current_database()를 호출하는 이유는 현재 접속된 database이름을 가져오려는 목적으로 보여 접속되는 데이터 베이스 이름이 반환되도록 options에서 설정한 databaseName을 반환하게 아래처럼 설정한다
memoryDb.public.registerFunction({
name: 'current_database',
implementation: () => options.database,
});
options에 있는 값이 아닌 별도의 값으로 세팅해도 좋다
다시 서버를 실행하면 에러가 또 발생한다
Failed SQL statement: SELECT "table_schema", "table_name", obj_description
(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ("table_schema" =
'public' AND "table_name" = 'test_entity');
해당 쿼리는 entities: []
에 등록된 entity가 현재 table이 존재하는지 확인하려는 의미로 보인다 pg-mem을 사용하기에 해당 쿼리에 반환값은 빈 배열이 되어야 한다
아래와 같이 해당 쿼리가 들어오면 빈 결과를 반환하도록 설정한다
memoryDb.public.interceptQueries((query) => {
if (
query.includes(
`SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE`,
)
) {
return [];
}
return null;
});
SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE
이 쿼리는 TypeOrm 버전이 변경되면서 변경될 가능성이 있어 TypeOrm 버전에 따라 적절한 조건값을 설정해야 한다 현재는0.3.20
버전 기준
이제 다시 서버를 실행하면 정상적으로 실행되게 된다 👍👍👍
최종 코드
TypeOrmModule.forRootAsync({
useFactory: () => {
return {
type: 'postgres',
host: 'your-host',
database: 'your-database-name',
username: 'your-username',
password: 'your-password',
entities: [TestEntity],
};
},
// useFactory에서 반환된 options값이 dataSourceFactory의 매개변수로 전달됩니다.
dataSourceFactory: (options) => {
const memoryDb = newDb();
memoryDb.public.registerFunction({
name: 'version',
implementation: () => 'PostgreSQL 16.1', // 버전 확인
});
memoryDb.public.registerFunction({
name: 'current_database',
implementation: () => options.database, // options에 있는 값이 아닌 별도 설정도 가능
});
memoryDb.public.interceptQueries((query) => {
if (
query.includes(
`SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE`,
)
) {
return [];
}
return null;
});
const dataSoruce = memoryDb.adapters.createTypeormDataSource({
...options,
// test 환경에 테이블이 자동으로 생성되도록 Synchronize를 true로 설정합니다.
synchronize: true,
});
return dataSoruce;
},
})