多次开玩笑 doMock 同一个方法

Jest doMock the same method multiple times

我想测试以下部分代码:

// ... code above

const created = async payload => {
  const model = await db.collection('models').doc(payload.model)
    .get() // <--- 1st .get() occurence

  if (!model.exists) {
    // Add product to the orphans collection
    await db.collection('orphans').doc(payload.sku).set(payload)
  } else {
    // Grab the categories field
    const categories = model.get('categories') // <--- 2nd .get() occurence

    // Product is either empty or does not exists at all
    if (!categories || categories.length < 1) {
      // Add product to the orphans collection
      await db.collection('orphans').doc(payload.sku).set(payload)
    } else {
      // Otherwise remove from the orphans collection
      await deleted(payload.sku)
    }
  }
}

我不知道如何在同一个回调中正确模拟文件两次。这是我得到的:

test.only('it should react when an event "created" has been fired', async () => {
  const spy = jest.fn()
  jest.doMock('@google-cloud/firestore', () => class {
    collection () {
      return {
        doc: () => {
          return {
            get: () => {
              return {
                exists: () => {
                  spy()
                }
              }
            },

            set: () => {
              spy()
            }
          }
        }
      }
    }
  })

  const observer = require('./product')
  await observer('created', {})

  await expect(spy.mock.calls.length).toBe(1)
})

我收到这个错误:

  ● it should react when an event "created" has been fired

    TypeError: model.get is not a function

      25 |   } else {
      26 |     // Grab the categories field
    > 27 |     const categories = model.get('categories')
         |                              ^
      28 |
      29 |     // Product is either empty or does not exists at all
      30 |     if (!categories || categories.length < 1) {

      at created (app/observers/product.js:27:30)
      at Object.<anonymous>.module.exports (app/observers/product.js:6:28)
      at Object.<anonymous> (app/observers/product.spec.js:34:3)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 skipped, 2 total
Snapshots:   0 total
Time:        0.147 s, estimated 1 s
Ran all test suites matching /app\/observers\/product.spec.js/i.

测试同一模拟 get() 方法的两个场景的工作解决方案是什么?

在您的代码中:

const model = await db.collection('models').doc(payload.model)
    .get() // <--- 1st .get() occurence

如果我们查看您的模拟,doc returns 的 get 方法:

              {
                exists: () => {
                  spy()
                }
              }

没有名为 get 的 属性,因此它是 undefined(而不是函数)。

我猜你只需要将这部分更改为:

              {
                exists: true, // can be false
                get: spy,
              }

你的问题应该已经解决了。

顺便说一句,您还可以将 set 方法的 mock 更改为 set: spy。或者你可以将它保持为 set: () => { spy() },但如果你想模拟它,你至少应该 return 这个值:set: () => { spy() }.

现在,关于如何正确模拟多次,这是您可以做的:

const observer = require('./product')

const spyGet = jest.fn()
const spySet = jest.fn() // I like having different mocks, if one function use get & set, tests will be clever & more readable if you use different spies

describe('on event "created" fired', () => {
  const categories = []

  beforeEach(() => {
    // I put mocks here to make test more readable
    jest.doMock('@google-cloud/firestore', () => class {
      collection () {
        return {
          doc: () => {
            return {
              get: () => {
                return {
                  exists: true,
                  get: spyGet,
                }
              },
              set: spySet
            }
          }
        }
      }
    })
    spyGet.mockResolvedValueOnce(categories) // you can also use mockResolvedValue, but mockResolvedValueOnce allow you to mock with different values on the same test & same mock
  })

  it.only('should get categories', async () => {
    await observer('created', {})

    // here's all the ways you can test it
    expect(spyGet).toBeCalledTimes(1)
    expect(spyGet.mock.calls.length).toBe(1)
    expect(spyGet).toBeCalledWith('categories')
    expect(spyGet).toHaveBeenNthCalledWith(1, 'categories')
  })
})

注意:如果你没有将它设置到 jest 配置中,你应该在测试之间手动重置和清除你的模拟(在 afterEach 或 beforeEach 中)。