使用带有 Avoriaz 的 AVA 在 Vue.js 中测试计算属性

Posted

技术标签:

【中文标题】使用带有 Avoriaz 的 AVA 在 Vue.js 中测试计算属性【英文标题】:Test computed property in Vue.js using AVA with Avoriaz 【发布时间】:2017-08-03 18:45:20 【问题描述】:

我正在尝试使用 AVA 和 Avoriaz 测试 Vue.js 组件的计算属性。我可以挂载组件并正常访问数据属性。

当我尝试访问计算属性时,该函数似乎没有该组件上数据的范围。

computed: 
  canAdd() 
    return this.crew.firstName !== '' && this.crew.lastName !== '';
  

我得到的错误是Error: Cannot read property 'firstName' of undefined

测试文件:

import Vue from 'vue';
import  mount 
from 'avoriaz';
import test from 'ava';
import nextTick from 'p-immediate';
import ComputedPropTest from '../../../js/vue-components/computed_prop_test.vue';

Vue.config.productionTip = false;

test.only('Should handle computed properties', async(t) => 
  const MOCK_PROPS_DATA = 
      propsData: 
        forwardTo: '/crew',
        crew: 
      
    ,
    wrapper = mount(ComputedPropTest, MOCK_PROPS_DATA),
    DATA = 
      crew: 
        firstName: 'Ryan',
        lastName: 'Gill'
      
    ;

  wrapper.setData(DATA);
  await nextTick();

  console.log('firstName: ', wrapper.data().crew.firstName); // Ryan

  console.log('isTrue: ', wrapper.computed().isTrue()); // true
  console.log('canAdd: ', wrapper.computed().canAdd()); // Errors

  t.true(wrapper.computed().isTrue());
);

组件:

<template>
  <div>
    <label for="firstName" class="usa-color-text-primary">First Name
      <i class="tooltipTextIcon fa fa-info-circle usa-color-text-gray" title="First name of crew."></i>
      <span class="required usa-additional_text usa-color-text-secondary-dark">Required</span>
    </label>
    <input id="firstName" type="text" class="requiredInput" name="firstName" v-model="crew.firstName" autofocus>
    <label for="lastName" class="usa-color-text-primary">Last Name
      <i class="tooltipTextIcon fa fa-info-circle usa-color-text-gray" title="Last name of crew."></i>
      <span class="required usa-additional_text usa-color-text-secondary-dark">Required</span>
    </label>
    <input id="lastName" type="text" class="requiredInput" name="lastName" v-model="crew.lastName" autofocus>
  </div>
</template>

<script>
  export default 
    name: 'crew-inputs',
    data() 
      return 
        crew: 
          firstName: '',
          lastName: ''
        
      
    ,
    computed: 
      canAdd() 
        return this.crew.firstName !== '' && this.crew.lastName !== '';
      ,
      isTrue() 
        return true;
      
    
  
</script>

isTrue 计算属性似乎有效,但不依赖组件中的任何数据。

【问题讨论】:

我看到您添加了控制台日志,以访问您正在测试的组件的数据和计算属性。他们是否按照您的期望报告?建议您在描述实际结果和预期结果的日志上方/旁边添加 cmets,而不是不加注​​释。 @wing 是的,日志符合预期。我刚刚用日志的值作为 cmets 更新了问题。谢谢。 好像不用Avoriazlet comp = new Vue(ComputedPropTest, MOCK_PROPS_DATA).$mount(); comp.crew = DATA; console.log(comp.canAdd);也能工作 我不相信箭头函数会阻止this 被正确绑定。您可以尝试更改一些箭头功能,但我怀疑它会起作用。您的测试代码是否在浏览器中运行?您正在使用 Ava,所以我假设它不是。我怀疑不在浏览器中运行 Avoriaz 会导致问题:我在浏览器和非浏览器环境中运行了简化的测试用例。浏览器通过,而非浏览器环境失败——尽管我仍在调查到底发生了什么以及为什么。 环境理论更新:我认为我正在排除它。我认为 Avoriaz 正在影响 this 与您不想要的东西的绑定。 【参考方案1】:

问题

发生了什么?

经过长时间的查看和讨论,看起来计算 getter 的 this 上下文被设置为意想不到的东西。由于意外的 this 上下文,this 不再引用 Vue 实例,导致组件属性无法访问。

您正在见证运行时错误

Error: Cannot read property 'firstName' of undefined

为什么会这样?

如果不深入了解 Avoriaz 和 Vue 的工作原理,我们就无法知道。我确实尝试使用以下最小、完整且可验证的示例进行更深入的调查。您或其他人可能想要更深入地了解它。

'use-strict';

import Vue from 'vue';
import  mount  from 'avoriaz';

const FooBar = 
  template: `
    <div> foobar </div>
  `,

  data() 
    return 
      foo: 'foo',
      bar: 'bar',
    ;
  ,

  computed: 
    foobar() 
      debugger;
      return `$this.foo $this.bar`;
    ,
  ,
;

const vueMountedCt = new Vue(FooBar).$mount();
const vueMountedVm = vueMountedCt;

const avoriazMountedCt = mount(FooBar);
const avoriazMountedVm = avoriazMountedCt.vm;

/**
 * Control case, accessing component computed property in the usual way as documented by Vue.
 *
 * @see @link https://vuejs.org/v2/guide/computed.html
 *
 * Expectation from log: 'foobar' (the result of the computed property)
 * Actual result from log: 'foobar'
 */
console.log(vueMountedVm.foobar);

/**
 * Reproduce Avoriaz's method of accessing a Vue component's computed properties.
 * Avoriaz returns the Vue instance's `$option.computed` when calling `wrapper.computed()`.
 *
 * @see @link https://github.com/eddyerburgh/avoriaz/blob/9882f286e7476cd51fe069946fee23dcb2c4a3e3/src/Wrapper.js#L50
 *
 * Expectation from log: 'foobar' (the result of the computed property)
 * Actual result from log: 'undefined undefined'
 */
console.log(vueMountedVm.$options.computed.foobar());

/**
 * Access Vue component computed property via Avoriaz's documented method.
 *
 * @see @link https://eddyerburgh.gitbooks.io/avoriaz/content/api/mount/computed.html
 *
 * Expectation from log: 'foobar' (the result of the computed property)
 * Actual result from log: 'undefined undefined'
 */
console.log(avoriazMountedCt.computed().foobar());

一些观察:

查看控制案例的调用堆栈(案例 1),您可以看到 Vue 内部将 this 上下文设置为 Vue 实例。

查看失败案例的调用堆栈,未设置计算函数的 this 上下文。

至于为什么会这样——我不知道。为了理解这一点,我认为我们需要知道为什么 vm.$options.computed 存在,核心 Vue 团队的计划用例以及我们遇到的行为是否符合预期。

对此我能做些什么?

您可以通过以下方式解决此问题

wrapper.computed().canAdd.call(wrapper.vm);

也可能建议您在Avoriaz 和/或Vue 中打开问题。

【讨论】:

您好,感谢您的详细回答。我是 avoriaz 的作者,我很想在下一个版本中修复这个错误 @Edd:不用担心。我不熟悉 Avoriaz 或 Vue 的内部结构,所以这份报告只是我对我认为正在发生的事情的观察和评论。希望它能帮助您解决这个问题。向下看的一个潜在途径是查看vm.$options.computed 的用途——我怀疑它可能不是可靠地为您提供计算属性的地方,但我无法确认核心团队对此的立场。 很好的解释。计算属性不容易测试。

以上是关于使用带有 Avoriaz 的 AVA 在 Vue.js 中测试计算属性的主要内容,如果未能解决你的问题,请参考以下文章

带有 appcompat 库 v7 的 ActionBar(ava.lang.IllegalStateException:您需要使用 Theme.AppCompat 主题)

无法使用 Jest 中的提交按钮触发 Vuetify 表单提交

ava基础MySQL存储过程 Java基础 JDBC连接MySQL数据库

无法在带有打字稿的 vue 中使用 Mixins

我在使用带有电子的 vue js 进行捆绑时遇到问题

在java中将带有命名空间的xml转换为json