How do you unit test a Vue.js functional component with a render function that returns any array of elements?

Ste*_*ert 4 javascript unit-testing vue.js

In Vue.js, a functional component can return multiple root nodes by using a render function that returns an array of createdElements.

export default {
  functional: true,
  props: ['cellData'],
  render: function (h, context) {
    return [
      h('td', context.props.cellData.category),
      h('td', context.props.cellData.description)
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)

This works great but I'm having trouble trying to create a unit test for such a component. Using shallowMount on the component results in [Vue warn]: Multiple root nodes returned from render function. Render function should return a single root node.

import { shallowMount } from '@vue/test-utils'
import Cell from '@/components/Cell'

wrapper = shallowMount(Cell, {
  context: {
    props: {
      cellData {
        category: 'foo',
        description: 'bar'
      }
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

This github issue suggests that the component needs to be wrapped in a single root node to actually render it, but trying that results in [vue-test-utils]: mount.context can only be used when mounting a functional component

import { shallowMount } from '@vue/test-utils'
import Cell from '@/components/Cell'

wrapper = shallowMount('<div><Cell></div>', {
  context: {
    props: {
      cellData {
        category: 'foo',
        description: 'bar'
      }
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

So how do I test a functional component that returns multiple root nodes?

Hus*_*him 7

您可以创建一个更高阶的透明包装器组件,Cell使用v-bind="$attrs"[1]v-on="$listeners"[2]将所有道具和事件侦听器传递给内部组件。然后您可以使用propsData将道具传递给包装器组件..

import { mount } from '@vue/test-utils'
import Cell from '@/components/Cell'

const WrappedCell = {
  components: { Cell },
  template: `
    <div>
      <Cell v-bind="$attrs" v-on="$listeners" />
    </div>
  `
}

const wrapper = mount(WrappedCell, {
  propsData: {
    cellData: {
      category: 'foo',
      description: 'bar'
    }
  }
});
Run Code Online (Sandbox Code Playgroud)


Mun*_*nna 5

您可以创建一个fragment_wrapper用于用 Fragment(多个根元素)包装您的组件。

//File: fragment_wrapper.js

exports.fragment_wrapper = function(FragmentComponent){
  const wrapper = {
    components: { FragmentComponent },
    props: FragmentComponent.props,
    template: `<div><FragmentComponent v-bind="$props" v-on="$listeners"/></div>`
  }
  return wrapper;  
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它来测试你所有的碎片化组件,如下所示:

import { mount } from '@vue/test-utils'
import { fragment_wrapper } from './fragment_wrapper'
import Cell from './components/Cell'


describe('Test Cell', () => {
  let WrappedCell = fragment_wrapper(Cell);
  const wrapper = mount(WrappedCell, {
    propsData: {
      cellData: {
        category: 'foo',
        description: 'bar'
      }
    }
  });

  it('renders the correct markup', () => {
    expect(wrapper.html()).toContain('<td>foo</td>')
  });
});
Run Code Online (Sandbox Code Playgroud)