Vue-test-utils 模拟从另一个组件获取响应

Posted

技术标签:

【中文标题】Vue-test-utils 模拟从另一个组件获取响应【英文标题】:Vue-test-utils Mock fetch response from another component 【发布时间】:2021-03-28 22:47:54 【问题描述】:

我正在尝试在一些 Vue 组件上使用 vue-test-utilsjest 设置单元测试,这些组件从另一个负责使用 @987654323 调用远程 API 的 Vue 组件 (ApiCalls.vue) 检索响应@。 进行 API 调用的组件返回此类型的对象:


  status: <fetch response status>,
  body: <fetch response body>

要测试的组件概述 (MyComponent.vue):

import ApiCalls from './ApiCalls.vue';

export default 
  data() 
    return 
      resultsFromAPI: null
    
  ,
  mounted() 
    this.getSomeData();
  ,
  methods: 
    async getSomeData() 
      const APIResponse = await ApiCalls.data().getSomeData();
      if (APIResponse.status === 200) 
        this.resultsFromAPI = APIResponse.body;
      
    
  

测试规范概述:

import  shallowMount  from '@vue/test-utils';
import MyComponent      from './MyComponent.vue';

const wrapper = shallowMount(MyComponent);

test('check data from API', () => 
  expect(wrapper.vm.resultsFromAPI).toBe(<stuff fromAPI>);
);

我试图像这样模拟函数getSomeData

wrapper.vm.getSomeData = jest.fn(() =>
  Promise.resolve(
    json: () => Promise.resolve(status: 200, body:  result: 'blabla' ),
  )
);

或者

const getSomeData = jest.fn(() =>
  Promise.resolve(
    json: () => Promise.resolve(status: 200, body:  result: 'blabla' ),
  )
);

但它不起作用,我在 vue-test-utils 和 jest 配置中找不到任何线索...... 任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

首先,你不应该在 Vue 构造函数上调用 data() 函数。我的意思是,你可以只是为了踢球,但你已经知道它会返回什么,对吧?这并不神秘。 “魔术”是构造函数在返回的 Vue 实例上为data() 的每个成员创建反应属性,其值不是undefined

现在,您的模拟尝试遇到了真正的问题:您似乎误解了模拟的目的,因此也误解了单元测试的目的。模拟是用始终按照外部系统预期行为的方式替换外部系统(到您的组件)。因为您应该只测试当前单元(组件)而不是其他任何东西。您的测试不应依赖于运行测试的机器是否可以访问 API、API 当前是否已关闭等...

但是永远不要模拟被测试组件的内容!

如果您这样做,您可能会在测试仍然通过时破坏您的测试组件,因为在测试中被破坏的方法被替换为可靠的模拟。您需要一个在您的方法中运行实际代码的测试,该测试在组件失败时失败,而在组件按预期运行时通过。

您应该测试组件是否为所有可能的输入生成预期的输出。

在您的情况下,您应该模拟 ApiCalls,使其行为符合您期望 ApiCalls 的行为,基本上您应该测试两件事:

ApiCalls.getSomeData 被调用一次(不多也不少),当当前组件被挂载时(并且使用正确的参数调用它) 当响应状态为200 时,组件的.resultsFromAPI 将填充返回的数据,否则保持null

您的组件不应该关心ApiCalls 是什么。换句话说,ApiCalls 应该是可模拟的。它可能是实际调用后端或后端模拟的东西。重要的是你的组件如何对ApiCalls 的方法返回的不同响应做出反应。

你可以测试的最后一件事是当ApiCalls 不可用时会发生什么,如果你认为这是一个实际的可能性(或者如果它永远不会解决会发生什么 - 它停止了)。这些是边缘情况,通常不包括在内,除非客户端有特定请求,否则应用程序应该从结构性错误中恢复(这种情况很少见)。


总而言之,永远不要编写在实际组件损坏时可以通过的测试。这是可能让你当场被解雇的事情之一(这是一个比喻——用失败时你可能失去的东西代替:客户、客户、生活、账户、声誉等),如果有任何重要的事情实际上取决于关于您的组件是否按预期执行。因为不仅您编写的测试未能捕捉到错误,而且它实际上谎称测试了组件(它从未做过 - 它测试组件模拟)。所以它给人一种虚假和危险的安全感。


使用in this answer 描述的方法来模拟ApiCalls' 方法。

注意: 平心而论,我本可以将您的问题标记为上述问题和许多其他问题的重复。以前有人问过这个问题。 但是,我选择发布这个冗长的警告作为答案(希望您和其他人可能会发现它有用)并概述不嘲笑应该嘲笑的内容和不正确测试的危险,因为我认为这是一个大问题UT 的写作方式。 这类错误(及其危险)的部分原因还在于现在强制要求使用 UT,而很少有编码公司在教授测试原理方面付出任何努力。

【讨论】:

@Joules,即使您的组件中的方法是async,您仍然不应该在测试中模拟该方法。您应该只模拟外部内容并检查您的组件是否发送(到模拟)它应该发送的内容,以及它是否正确处理响应。为了更好地理解原因:如果您不小心在组件的方法中键入了某些内容并且它只是中断了,您希望测试失败。如果您模拟该方法,则测试将通过,而最终用户将获得一个损坏的应用程序。

以上是关于Vue-test-utils 模拟从另一个组件获取响应的主要内容,如果未能解决你的问题,请参考以下文章

使用 element-ui 和 vue-test-utils 模拟选择

vue-test-utils 的 setData 没有重新更新组件

使用 vue-test-utils / jest 触发 Quasar QBtn 点击

如何在单元测试期间使用 vue-test-utils 和 jest 模拟 mixin?

如何在单元测试期间使用带有 shallowMount 的 vue-test-utils 找到元素组件?

vue-test-utils - 如何处理 $refs?