【问题标题】:Unit test Vuex getters that depend on other getters单元测试依赖于其他 getter 的 Vuex getter
【发布时间】:2023-04-09 14:16:01
【问题描述】:

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

describe('getFoo', () => {

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

});

describe('getFancyNames', () => {

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

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

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

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

【问题讨论】:

    标签: javascript unit-testing vue.js vuex jestjs


    【解决方案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: 'stackoverflow'
    }
    
    describe('getFancyNames', () => {
      const getFoo = jest.fn()
      getFoo.mockReturnValueOnce('foo: stackoverflow')
      getFoo.mockReturnValueOnce('bar: stackoverflow')
    
      it('returns a collection of fancy names', () => {
        expect(getters.getFancyNames(state, { getFoo })).toEqual([
          'foo: stackoverflow',
          'bar: stackoverflow'
        ])
      })
    })
    

    【讨论】:

      【解决方案3】:

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

      const state = {
        name: 'stackoverflow'
      }
      
      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: stackoverflow',
            'bar: stackoverflow'
          ])
        })
      })
      

      额外

      如果您确实需要调用其他 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!
      

      【讨论】:

        猜你喜欢
        • 2018-03-08
        • 2020-04-02
        • 2017-06-03
        • 2012-03-20
        • 1970-01-01
        • 2021-08-25
        • 1970-01-01
        • 2019-10-16
        • 2018-08-18
        相关资源
        最近更新 更多