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

Posted

技术标签:

【中文标题】单元测试依赖于其他 getter 的 Vuex getter【英文标题】:Unit test Vuex getters that depend on other getters 【发布时间】:2018-09-29 05:14:34 【问题描述】:

我已经设法测试了与其他代码隔离的 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: '***'
;

describe('getFoo', () => 

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

);

describe('getFancyNames', () => 

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

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

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

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

【问题讨论】:

【参考方案1】:

我同意你的观点,在你的模拟中引用实际的合作者违背了模拟的目的。因此,我会直接返回您希望您的合作者返回的任何内容。

在您的示例中,不要这样做:

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

您只需输入getters.getFoo(state) 将返回的任何内容:

const _getters = 
    getFoo: 'foobar' 

如果您有一个带有附加参数的 getter,您只需返回一个返回常量的函数:

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

【讨论】:

问题是您的测试实际上会运行原始的 getter,然后测试不会通过,因为您的 mock 包含生成与 getter 输出不同的输出。 不确定我是否在关注。您的测试运行将模拟的 getter 作为参数的 getter。我实际上在我的一个项目中使用这种方法。你能举个例子说明这会如何破坏你的测试吗? 哦,对不起,我想我现在看到了。您的 getFancyNames 方法使用不同的参数两次调用相同的 getter。我的例子意味着你在这两种情况下都会返回foobar 感谢您的宝贵时间。你是对的它有效,我想在大多数情况下返回相同的值将是解决方案,甚至是最好的解决方案,因为你想隔离你的吸气剂测试。我已经发布了一个答案,因为我找到了一种使用jest 实现此目的的方法,以防您感到好奇;)【参考方案2】:

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

mockReturnValueOncemockReturnValue

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

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

const state = 
  name: '***'


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

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

【讨论】:

【参考方案3】:

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

const state = 
  name: '***'


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: ***',
      'bar: ***'
    ])
  )
)

额外

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

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!

【讨论】:

以上是关于单元测试依赖于其他 getter 的 Vuex getter的主要内容,如果未能解决你的问题,请参考以下文章

无法读取未定义的属性“getters” - 带有笑话的 VueJS 单元测试

如何在 Mocha 单元测试中访问命名空间的 Vuex getter

Vue 单元测试:如何使用 props、vuex store、watchers、getter 等测试复杂组件

使用 sinonjs 存根 vuex getter

Vuex - Getter

Vuex 计算属性仅适用于 getter