TypeORM @Transaction 时使用 Jest 进行单元测试


TypeORM @Transaction 时使用 Jest 进行单元测试

所有细节代码见这里:https://github.com/wangjiegulu/express_server_demo

当使用 Service 使用 @Transcation 进行事务操作时,无法进行单元测试。

AccountService

1
2
3
4
5
6
7
8
9
@Service()
export default class AccountService {

@Transaction()
async loginByWechatMiniApp(loginDetail: any, @TransactionManager() manager?: EntityManager) {
// 业务代码省略
}

}

正常运行时没有问题,但是在执行如下单元测试时报错:

AccountService.test.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
describe("loginByWechatMiniApp", ()=>{

beforeEach(()=>{
mockRpGet.mockClear()
mockFindOne.mockClear()
mockSave.mockClear()
})

test("login success (user not exist)", async ()=>{
// session result validate
mockRpGet.mockReturnValue(Promise.resolve(sessionResult_validate))
mockFindOne.mockReturnValueOnce(Promise.resolve(null)) // find userWechat

const user = new User()
mockSave.mockReturnValueOnce(Promise.resolve(user)) // save user
const userWechat = new UserWechat()
mockSave.mockReturnValueOnce(Promise.resolve(userWechat)) // save userWechat

mockFindOne.mockReturnValueOnce(Promise.resolve(null)) // find userLogin

const userLogin = new UserLogin()
userLogin.token = "tokentoken"
mockSave.mockReturnValueOnce(Promise.resolve(userLogin)) // save userLogin

let result = await accountService.loginByWechatMiniApp(loginDetail_validate, entityManager)

expect(mockRpGet).toBeCalledTimes(1)
expect(mockFindOne).toBeCalledTimes(2)
expect(mockSave).toBeCalledTimes(3)
expect(result.user).toEqual(user)
expect(result.loginInfo).toEqual(userLogin)
})
})

会出现如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
loginByWechatMiniApp › login success (login success (user not exist))

ConnectionNotFoundError: Connection "default" was not found.

at new ConnectionNotFoundError (src/error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.get (src/connection/ConnectionManager.ts:40:19)
at Object.getConnection (src/index.ts:253:35)
at AccountService.descriptor.value (src/decorator/transaction/Transaction.ts:98:24)
at src/test/bll/AccountService.test.ts:219:43
at src/test/bll/AccountService.test.ts:8:71
at __awaiter (src/test/bll/AccountService.test.ts:4:12)
at Object.<anonymous> (src/test/bll/AccountService.test.ts:202:76)

这是因为 @Transaction() 中会执行 getConnection() 方法,但是因为单元测试没有执行数据库连接,所以会找不到可用的 connection,报这个错,因此,为了单元测试跑通,需要在运行单元测试时,忽略掉这个 @Transcation() 中会执行的逻辑,因为所有的数据库操作我们都会进行 mock。

所以,如下,我们可以通过自定义 decoratorXTransaction) 来处理,创建 typeorm.ext.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Transaction, TransactionManager } from 'typeorm';
import { isTestEnv } from '../util/appUtil';

export function XTransaction(connectionOrOptions?) {
let result = null
if (!isTestEnv()) {
result = Transaction(connectionOrOptions)
}
return function (target, methodName, descriptor) {
if (!isTestEnv()) {
result(target, methodName, descriptor)
}
}
}

export function XTransactionManager() {
let result = null
if (!isTestEnv()) {
result = TransactionManager()
}
return function (object, methodName, index) {
if (!isTestEnv()) {
result(object, methodName, index)
}
}
}

再编辑 package.json:

1
2
3
4
5
6
7
8
9
{
// ...
"scripts": {
// ...
"test": "NODE_ENV=test jest"
// ...
},
// ...
}

appUtil.ts

1
2
3
export let isTestEnv = () => {
return getEnv() === 'test'
}

然后替换掉 AccountService.ts 中的 @Transaction 即可:

1
2
3
4
5
6
7
8
9
@Service()
export default class AccountService {

@XTransaction()
async loginByWechatMiniApp(loginDetail: any, @XTransactionManager() manager?: EntityManager) {
// 业务代码省略
}

}

最终的代码:



来源博客:Wang Jie's Blog's Blog
本文链接:https://blog.wangjiegulu.com/2020/02/13/fix-jest-typeorm-transaction-decorator/
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处。