简单提一下这个问题,先讲思路,后面再进行深入的探索和解析和发散
事情发生在笔者开发某个typescript服务端项目的过程中。笔者需要将一个基于typeorm的读写数据库操作进行异步处理,也就是不阻塞当前线程。
大体是这样的一个行文:- // 用于typeorm的models文件中
- class Person {
- @Column({ nullable: true })
- code: string | null = null;
- async initCode() {
- if (!this.code) return;
-
- this.code = randomAlphabet(); // 随机生成的code
- }
- }
- // 读写数据库文件中, eg: repos/person.ts
- async function generateCode(person: Person): Promise<Person> {
- try {
- person.initCode();
- } catch (e) {
- // error catcher
- }
- }
- // 处理逻辑的controllers中,eg: controllers/personController.ts
- async function generateCodeForPerson(ctx, next): Promise<void> {
- const person = await getPerson(ctx.params.id);
- void generateCode(person); // 这里不做await,选择异步处理
- ctx.code = 200;
- }
复制代码 那么好,由于该项目比较重要,所以笔者所在项目组使用jest进行e2e测试,以确保api可用。同时会部署CI,用于在pull request过程中进行代码自测。
其中测试generateCode大致是这么一个走法:- describe('testGenerateCode', async () => {
- let runInBackgroundMock: jest.SpyInstance<
- void,
- [callback: () => Promise<unknown>]
- >; // 这个是jest用于劫持运行函数中的函数后,将函数中的异步改同步的操作
- beforeAll(async () => {
- // 此处,将测试函数中所运行的utils.runInBackground方法进行mock,取消其异步性,将在未来笔记中深入讲解
- runInBackgroundMock = jest
- .spyOn(utils, 'runInBackground')
- .mockImplementation();
- initDB(); // 初始化CI过程中连接测试DB的Client,占用DB连接资源
- })
- /**
- * 此处不止一个describe
- */
- describe('should successful', async () => {
- await testGenerateCodeForPerson(); // 其中调用了generateCodeForPerson,但是比较深,所以当时没发现
- })
- afterAll(async () => {
- closeDB(); // 关掉DBClient,释放DB连接资源
- })
- });
复制代码 细心的读者应该能够但从中发现问题了:万一没执行完generateCode的内容,就closeDB了,那不就出问题了?
没错!笔者遇到这个问题了,但第一时间没有意识到问题来自笔者的代码,下面是CI的报错:
此处插入题外话,笔者发现CI执行每个e2e的测试文件的次序是固定的,走到generateCode的下一个e2e测试任务就马上会遇到该问题,以至于笔者一直以为是任务A出了问题,但本地单独jest该任务永远都是Pass的,致使笔者一度有些...
但是经过前辈的指导,其中一个就是让笔者在本地使用CI所使用的命令,对所有e2e测试文件进行测试,而不是对单个e2e测试文件进行测试。同时,前辈让笔者留意是否与DB之间的连接关闭之后仍然有着读写DB的操作。
也就是说:- # 用这个
- yarn jest tests/e2e
- # 而不是用这个
- yarn jest tests/e2e/generate_code_for_person.ts
复制代码 得益于本地运行命令,笔者发现测试文件的执行顺序并不是固定的,自然而然地发现每一个跟在generateCode的后e2e测试任务就会QueryFailedError。于是进行了注释debug方法,也就是每个describe都注释一下,最后锁定了问题出在generateCode函数中。
由此,笔者悻悻地将代码给改了,给controllers/personController.ts中加了一个utils.runInBackground。
也就是:- // 处理逻辑的controllers中,eg: controllers/personController.ts
- async function generateCodeForPerson(ctx, next): Promise<void> {
- const person = await getPerson(ctx.params.id);
- utils.runInBackGround(async () => {
- await generateCode(person); // 这里做不做await好像都没所谓了。
- });
- ctx.code = 200;
- }
复制代码 于是乎就这么改了,CI就过了。
所以后续会继续记录在进行和使用jest、postgres sql、typeorm、数据库表设计过程中的的所见所闻。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |