单元测试依赖于其他 getter 的 Vuex getter

Unit test Vuex getters that depend on other getters

我已经设法测试了与其他代码隔离的 Vuex getter。当 getter 依赖于其他 getter 时,我现在面临一些问题,请参见以下示例:

getters.js

export const getters = {

  getFoo(state) => prefix {
    return `${prefix}: ${state.name}`;
  },

  getFancyNames(state, getters) {
    return [
      getters.getFoo('foo'),
      getters.getFoo('bar')
    ]
  }
}

getters.spec.js

import { getters } = './getters';

const state = {
  name: 'Whosebug'
};

describe('getFoo', () => {

  it('return name with prefix', () => {
    expect(getters.getFoo(state)('name')).toBe('name: Whosebug');
  });

});

describe('getFancyNames', () => {

  // mock getters
  const _getters = {
    getFoo: getters.getFoo(state)
  }

  it('returns a collection of fancy names', () => {
    expect(getters.getFancyNames(state, _getters)).toEqual([
      'foo: Whosebug',
      'bar: Whosebug'
    ]);
  });
});

当被测试的 getter 依赖于其他具有参数的 getter 时,这意味着我在模拟中引用了原始 getter.getFoo,这打破了模拟的想法,因为测试开始相互关联。当 getter 增长时,依赖图有多个级别,这会使测试变得复杂。

也许这是要走的路,只是想检查一下我没有遗漏任何东西...

我同意你的看法,在你的模拟中引用实际的合作者违背了模拟的目的。所以我会直接 return 任何你想让你的合作者 return 的东西。

在你的例子中,不要做这样的事情:

// mock getters
const _getters = {
  getFoo: getters.getFoo(state)
}

您只需输入 getters.getFoo(state) 会 return:

const _getters = {
    getFoo: 'foobar' 
}

如果您有一个 getter 需要一个额外的参数,您只需 return 一个 return 是常量的函数:

const _getters = {
    getFoo: x => 'foobar',
}

由于我使用的是 Jest,jest 模拟函数中有一个选项让我们在调用时指定 return 值:

mockReturnValueOncemockReturnValue

可在此处找到更多信息:https://facebook.github.io/jest/docs/en/mock-functions.html#mock-return-values

使用与问题中相同的代码可以这样解决:

const state = {
  name: 'Whosebug'
}

describe('getFancyNames', () => {
  const getFoo = jest.fn()
  getFoo.mockReturnValueOnce('foo: Whosebug')
  getFoo.mockReturnValueOnce('bar: Whosebug')

  it('returns a collection of fancy names', () => {
    expect(getters.getFancyNames(state, { getFoo })).toEqual([
      'foo: Whosebug',
      'bar: Whosebug'
    ])
  })
})

我发现的一种更简洁的方法是创建您自己的模拟 getters 对象。仅当 getter 像问题一样使用未更改的 state 时才有效。

const state = {
  name: 'Whosebug'
}

describe('getFancyNames', () => {
  const mockedGetters = {
    ...getters,  // This can be skipped
    getFoo: getters.getFoo(state),  // We only overwrite what is needed
  };

  it('returns a collection of fancy names', () => {
    expect(getters.getFancyNames(state, mockedGetters)).toEqual([
      'foo: Whosebug',
      'bar: Whosebug'
    ])
  })
})

额外

如果您确实需要调用其他 getter 函数,只需将模拟的 getters 对象传递给另一个模拟的 getters 对象即可。听起来比实际情况更糟。

getters.py

export const getters = {

  getBar(state) = {   // new extra hard part!
    return state.bar,
  },

  getFoo(state, getters) => prefix {
    return `${prefix}: ${state.name} with some ${getters.getBar}`;
  },

  getFancyNames(state, getters) {
    return [
      getters.getFoo('foo'),
      getters.getFoo('bar')
    ]
  }
}
const _mockedGetters = {
  ...getters,  // This can be skipped
  getFoo: getters.getFoo(state),  // We only overwrite what is needed
};

const mockedGetters = {
  .._mockedGetters,  // Use the mocked object!
  getBar: getters.getBar(state, _mockedGetters),  // We only overwrite what is needed
};

// continue down the line as needed!