英文:
Jest database test not terminating with testcontainers
问题
I am trying to test my database in my Node application (Typescript). I am using Postgres 15 and Testcontainers to test my database. My code works fine manually and my clients are being released, but for some reason my tests don't terminate.
I want to test the createUser
method which uses getUserById
, which also uses getTagsByUserId
.
In DatabaseConnection
I have a Client Pool. With getClient
I return a Client from the pool.
This is my test:
import { UsersRepository } from "../../main/repository/users.repository";
import { Tag, User } from "../../main/schema/types";
import { Pool, PoolClient } from "pg";
import {
TestContainer,
StartedTestContainer,
PostgreSqlContainer, Wait, StartedPostgreSqlContainer
} from "testcontainers";
import { DatabaseConnection } from "../../main/repository/DatabaseConnection";
const fs = require('fs')
jest.useFakeTimers()
const initSqlScript = fs.readFileSync('./sql/init.sql').toString()
describe('User Repository tests', () => {
const port: number = process.env.DB_PORT ? +process.env.DB_PORT : 5432
let pool: Pool
let container: StartedPostgreSqlContainer
beforeAll(async () => {
container = await new PostgreSqlContainer("postgres:15")
.withDatabase(process.env.POSTGRES_DB || 'test')
.withUsername(process.env.POSTGRES_USER || 'test')
.withPassword(process.env.POSTGRES_PASSWORD || 'test')
.withExposedPorts(port)
.start();
pool = new Pool({connectionString: container.getConnectionUri()})
const client = await pool.connect()
await client.query(initSqlScript) // initializes my database
await client.release()
await DatabaseConnection.changeConnection(container.getConnectionUri()) // sets the connection for my DatabaseConnection to the test container
})
const userRepository = new UsersRepository()
const originalUser: User = {
dateOfBirth: "01-01-2000",
email: "test",
nickname: "test",
password: "test",
username: "test",
pending: true
}
afterEach(async () => {
await userRepository.clear()
})
it('should create a user in DB and return user with ID', async () => {
const createdUser = await userRepository.createUser(originalUser)
expect(createdUser.username).toBe(originalUser.username);
expect(createdUser.email).toBe(originalUser.email);
expect(createdUser.nickname).toBe(originalUser.nickname);
expect(createdUser.password).toBe(originalUser.password);
expect(createdUser.dateOfBirth).toBe(originalUser.dateOfBirth);
expect(createdUser.pending).toBe(originalUser.pending);
expect(typeof createdUser.userId).toBe("string")
});
})
However, when I run my test, it does not terminate. I have checked the database and the user is created. I have also tried just returning the user object I have as input instead of getting the user from the database, but that also does not work.
I am logging any connections to my pool and it only says 1 idle connection and no active connections. When debugging, all of my clients are released.
Also, it works when I just use my local database, but I don't want that.
英文:
I am trying to test my database in my Node application (Typescript). I am using Postgres 15 and Testcontainers to test my database. My code works fine manually and my clients are being released, but for some reason my tests don't terminate.
I want to test the createUser
method which uses getUserById
, which also uses getTagsByUserId
.
async createUser(user: User): Promise<User> {
let client: PoolClient | undefined
try {
client = await DatabaseConnection.getClient()
await client.query('BEGIN')
const res = await client.query(this.CREATE_USER_QUERY, [user.username, user.password, user.nickname, user.email, user.dateOfBirth, user.pending])
const userId = res.rows[0].user_id
user.userId = userId
if (user.tags) {
for (const tag of user.tags) {
await client.query(this.CREATE_TAGS_QUERY, [userId, tag.toString()])
}
}
await client.query('COMMIT')
return await this.getUserById(userId)
} catch (e) {
if (client) await client.query('ROLLBACK')
throw e
} finally {
if (client) await client.release()
}
}
async getUserById(userId: string): Promise<User> {
let client: PoolClient | undefined
try {
client = await DatabaseConnection.getClient()
const res = await client.query(this.SELECT_USER_BY_ID_QUERY, [userId])
const user: User = {
dateOfBirth: res.rows[0].date_of_birth,
email: res.rows[0].email,
nickname: res.rows[0].nickname,
password: res.rows[0].password,
pending: res.rows[0].pending,
private: res.rows[0].private,
tags: [],
userId: res.rows[0].user_id,
username: res.rows[0].username
}
user.tags = await this.getTagsByUserId(userId)
return user
} catch (e) {
throw e
} finally {
if (client) await client.release()
}
}
private async getTagsByUserId(userId: string): Promise<Tag[]> {
let client: PoolClient | undefined
try {
client = await DatabaseConnection.getClient()
const res = await client.query(this.GET_TAGS_QUERY, [userId])
return res.rows.map(row => row.user_tag)
} catch (e) {
throw e
} finally {
if (client) await client.release()
}
}
In DatabaseConnection
I have a Client Pool. With getClient
i return a Client from the pool.
This is my test:
import {UsersRepository} from "../../main/repository/users.repository";
import {Tag, User} from "../../main/schema/types";
import {Pool, PoolClient} from "pg";
import {
TestContainer,
StartedTestContainer,
PostgreSqlContainer, Wait, StartedPostgreSqlContainer
} from "testcontainers";
import {DatabaseConnection} from "../../main/repository/DatabaseConnection";
const fs = require('fs')
jest.useFakeTimers()
const initSqlScript = fs.readFileSync('./sql/init.sql').toString()
describe('User Repository tests', () => {
const port: number = process.env.DB_PORT ? +process.env.DB_PORT : 5432
let pool: Pool
let container: StartedPostgreSqlContainer
beforeAll(async () => {
container = await new PostgreSqlContainer("postgres:15")
.withDatabase(process.env.POSTGRES_DB || 'test')
.withUsername(process.env.POSTGRES_USER || 'test')
.withPassword(process.env.POSTGRES_PASSWORD || 'test')
.withExposedPorts(port)
.start();
pool = new Pool({connectionString: container.getConnectionUri()})
const client = await pool.connect()
await client.query(initSqlScript) // initializes my database
await client.release()
await DatabaseConnection.changeConnection(container.getConnectionUri()) // sets the connection for my DatabaseConnection to the test container
})
const userRepository = new UsersRepository()
const originalUser: User = {
dateOfBirth: "01-01-2000",
email: "test",
nickname: "test",
password: "test",
username: "test",
pending: true
}
afterEach(async () => {
await userRepository.clear()
})
it('should create a user in DB and return user with ID', async () => {
const createdUser = await userRepository.createUser(originalUser)
expect(createdUser.username).toBe(originalUser.username);
expect(createdUser.email).toBe(originalUser.email);
expect(createdUser.nickname).toBe(originalUser.nickname);
expect(createdUser.password).toBe(originalUser.password);
expect(createdUser.dateOfBirth).toBe(originalUser.dateOfBirth);
expect(createdUser.pending).toBe(originalUser.pending);
expect(typeof createdUser.userId).toBe("string")
});
})
However, when i run my test, it does not terminate. I have checked the database and the user is created. I have also tried just returning the user object i have as input instead of getting the user from the database, but that also does not work.
I am logging any connections to my pool and it only says 1 idle connection and no active connections. When debugging, all of my clients are released.
Also, it works when I just use my local database, but I don't want that.
答案1
得分: 0
我自己找到了解决方法!我配置了Jest来使用虚拟计时器,因为我在某处读到这样做可以帮助处理超时异常。我不知道这正是导致问题的原因!
英文:
Ok so I figured it out on my own! I configured Jest to use fake timers because I read somewhere that this would help with timeout exceptions. Little did I know that this was causing my problems!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论