为什么 jest mockResolvedValueOnce 多次调用 returns 相同的值?
Why does jest mockResolvedValueOnce called multiple times returns the same value?
我有一个 class 方法,我多次触发 @google-cloud/firestore
。我想多次通过相同的 .get()
方法模拟调用。
多次使用具有与 return 不同值的 mockResolvedValueOnce
,第二个值将被忽略。
jest.doMock('@google-cloud/firestore', () => class {
collection () {
return {
get: jest.fn().mockResolvedValue({
docs: []
}),
doc: () => {
return {
set: jest.fn(),
get: jest.fn().mockResolvedValueOnce({})
}
},
limit: () => {
return {
get: jest.fn().mockResolvedValue({ empty: true })
}
},
onSnapshot: jest.fn(),
select: () => {
return {
get: jest.fn() // <------------ MULTIPLE CALLS CHAINED BELOW
.mockResolvedValueOnce({
size: 1
}).mockResolvedValueOnce({
size: 2
})
}
}
}
}
})
当我 console.log(snapshot.size)
时,它 return 向我两次调用相同的值“1”。
if (isEmptyModels || isStatsEmptyModels) {
// ...
console.log(' [STATS][MODELS] - Fulfilling the counters')
await Database.collection('models').select('id').get().then(snapshot => {
console.log(snapshot.size) // <--------- 1st call
this.fields.models.count = snapshot.size
this.fields.models.linked = snapshot.size
})
// ...
}
if (isEmptyProducts1P || isStatsEmptyProducts1P) {
// ...
console.log(' [STATS][PRODUCTS1P] - Fulfilling the counters')
await Database.collection('products1P').select('isMaintained').get().then(snapshot => {
console.log(snapshot.size) // <--------- 2nd call
snapshot.forEach(doc => {
if (doc.data().isMaintained) {
// ...
}
})
// ...
})
// ...
}
为什么会这样,哪里做错了?
错误信息是:
console.log
[STATS][MODELS] - Fulfilling the counters
at Statistics.fulfillProductsCount (app/services/statistics/index.js:95:15)
console.log
1
at app/services/statistics/index.js:97:17
console.log
[STATS][PRODUCTS1P] - Fulfilling the counters
at Statistics.fulfillProductsCount (app/services/statistics/index.js:106:15)
console.log
1
at app/services/statistics/index.js:108:17
TypeError: snapshot.forEach is not a function
117 | await Database.collection('products1P').select('isMaintained').get().then(snapshot => {
118 | console.log(snapshot.size)
> 119 | snapshot.forEach(doc => {
| ^
120 | if (doc.data().isMaintained) {
121 | this.fields.products1P.maintained += 1
122 | } else {
at app/services/statistics/index.js:119:18
发生这种情况是因为每次调用 Database.collection()
时,它都会创建一个新对象,并且作为一个新对象,这是第一次调用其属性。它对集合中的其他函数也有效。
我的意思是 Database.collection 是一个函数,return 是一个包含其他函数的对象,return 对象包含模拟的属性。通过这种方式进行模拟,您将永远无法使用 mock...ValueOnce。但是,我看到有两种方法可以“绕过”这个问题:
1 - 简短但矛盾的方法
您可以使用 .mockReturnThis()
来避免进入深度模拟 objects/functions,但在处理具有多次相同方法名称的“fat”类 时可能会很快发生冲突.在模拟可链接方法时也很有用(例如:ORM 查询与 .find().filter().sort()...)。
jest.doMock('@google-cloud/firestore', () => class {
collection = jest.fn().mockReturnThis();
select = jest.fn().mockReturnThis();
get = jest.fn().mockResolvedValueOnce({ size: 1 }).mockResolvedValueOnce({ size: 2 });
})
2 - 漫长但可行的方法
模拟整个收集方法一次,而不是只模拟一次 collection().select().get()
。
Database.collection.prototype.mockReturnValueOnce({
select: () => {
get: () => ({ size: 1 })
}
}).mockReturnValueOnce({
select: () => {
get: () => ({ size: 2 })
}
})
--> 您将需要访问模拟的 Class 并模拟原型的方法“集合”(collection = jest.fn())。
我有一个 class 方法,我多次触发 @google-cloud/firestore
。我想多次通过相同的 .get()
方法模拟调用。
多次使用具有与 return 不同值的 mockResolvedValueOnce
,第二个值将被忽略。
jest.doMock('@google-cloud/firestore', () => class {
collection () {
return {
get: jest.fn().mockResolvedValue({
docs: []
}),
doc: () => {
return {
set: jest.fn(),
get: jest.fn().mockResolvedValueOnce({})
}
},
limit: () => {
return {
get: jest.fn().mockResolvedValue({ empty: true })
}
},
onSnapshot: jest.fn(),
select: () => {
return {
get: jest.fn() // <------------ MULTIPLE CALLS CHAINED BELOW
.mockResolvedValueOnce({
size: 1
}).mockResolvedValueOnce({
size: 2
})
}
}
}
}
})
当我 console.log(snapshot.size)
时,它 return 向我两次调用相同的值“1”。
if (isEmptyModels || isStatsEmptyModels) {
// ...
console.log(' [STATS][MODELS] - Fulfilling the counters')
await Database.collection('models').select('id').get().then(snapshot => {
console.log(snapshot.size) // <--------- 1st call
this.fields.models.count = snapshot.size
this.fields.models.linked = snapshot.size
})
// ...
}
if (isEmptyProducts1P || isStatsEmptyProducts1P) {
// ...
console.log(' [STATS][PRODUCTS1P] - Fulfilling the counters')
await Database.collection('products1P').select('isMaintained').get().then(snapshot => {
console.log(snapshot.size) // <--------- 2nd call
snapshot.forEach(doc => {
if (doc.data().isMaintained) {
// ...
}
})
// ...
})
// ...
}
为什么会这样,哪里做错了?
错误信息是:
console.log
[STATS][MODELS] - Fulfilling the counters
at Statistics.fulfillProductsCount (app/services/statistics/index.js:95:15)
console.log
1
at app/services/statistics/index.js:97:17
console.log
[STATS][PRODUCTS1P] - Fulfilling the counters
at Statistics.fulfillProductsCount (app/services/statistics/index.js:106:15)
console.log
1
at app/services/statistics/index.js:108:17
TypeError: snapshot.forEach is not a function
117 | await Database.collection('products1P').select('isMaintained').get().then(snapshot => {
118 | console.log(snapshot.size)
> 119 | snapshot.forEach(doc => {
| ^
120 | if (doc.data().isMaintained) {
121 | this.fields.products1P.maintained += 1
122 | } else {
at app/services/statistics/index.js:119:18
发生这种情况是因为每次调用 Database.collection()
时,它都会创建一个新对象,并且作为一个新对象,这是第一次调用其属性。它对集合中的其他函数也有效。
我的意思是 Database.collection 是一个函数,return 是一个包含其他函数的对象,return 对象包含模拟的属性。通过这种方式进行模拟,您将永远无法使用 mock...ValueOnce。但是,我看到有两种方法可以“绕过”这个问题:
1 - 简短但矛盾的方法
您可以使用 .mockReturnThis()
来避免进入深度模拟 objects/functions,但在处理具有多次相同方法名称的“fat”类 时可能会很快发生冲突.在模拟可链接方法时也很有用(例如:ORM 查询与 .find().filter().sort()...)。
jest.doMock('@google-cloud/firestore', () => class {
collection = jest.fn().mockReturnThis();
select = jest.fn().mockReturnThis();
get = jest.fn().mockResolvedValueOnce({ size: 1 }).mockResolvedValueOnce({ size: 2 });
})
2 - 漫长但可行的方法
模拟整个收集方法一次,而不是只模拟一次 collection().select().get()
。
Database.collection.prototype.mockReturnValueOnce({
select: () => {
get: () => ({ size: 1 })
}
}).mockReturnValueOnce({
select: () => {
get: () => ({ size: 2 })
}
})
--> 您将需要访问模拟的 Class 并模拟原型的方法“集合”(collection = jest.fn())。