Skip to content

Latest commit

 

History

History
222 lines (182 loc) · 5.77 KB

tests.md

File metadata and controls

222 lines (182 loc) · 5.77 KB

Tests

  • Keep the description blocks simple: explain what the test is about, not how you tested it.
// ❌ Wrong
it(
  'focuses the previous element when the user presses the down arrow key, the next element when the user presses the up arrow key and loops, the first element when the user is in the last element and presses the down arrow key, and the last element when the user is in the first element and presses the up arrow key '
);

// ✅ Good
it('changes the selected element when navigating with the arrow keys');
  • The sentence must make sense when concatenating it with the it test method.
// ❌ Wrong
it('the element is focused when the dropdown is open');

// ✅ Good
it('focuses the element when the dropdown is open');
  • If the text description is longer than the max. line length, disable the eslint warning.
// ❌ Wrong
it(
  'emits `UserAlmostReachedScrollEnd` and`UserReachedScrollEnd` when the user ' +
    'scrolls to the bottom'
);

// ✅ Good
// eslint-disable-next-line max-len
it(
  'emits `UserAlmostReachedScrollEnd` and`UserReachedScrollEnd` when the user scrolls to the bottom'
);

Factory functions

Use factory functions whenever it is possible to DRY in the mount process.

  • Include every property passed to the mount function that is used in the tests.
// ❌ Wrong
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';

it('does this', () => {
  const wrapper = mount(TestComponentA, {
    propsData: { propA: 'valueA' },
    components: { TestComponentB }
  });
});

it('does that', () => {
  const wrapper = mount(TestComponentA, {
    propsData: { propA: 'valueB' },
    components: { TestComponentB }
  });
});

// ✅ Good
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';

const factory = propsData => {
  return mount(TestComponentA, {
    propsData,
    components: { TestComponentB }
  });
};

it('does this', () => {
  const wrapper = factory({ propA: 'valueA' });
});

it('does that', () => {
  const wrapper = factory({ propA: 'valueB' });
});
  • In addition, you can return more properties including repeated functionality to keep the test simple. Then you have an API with methods to test the component more easily or just access to data like the default data set in the component.
// ❌ Wrong
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';
import Vue from 'vue';

const factory = propsData => {
  return mount(TestComponentA, {
    propsData,
    components: { TestComponentB }
  });
};

it('does this', async () => {
  const wrapper = factory({ propA: 'valueA' });
  wrapper.findComponent(TestComponentB).trigger('click');
  await Vue.nextTick();
});

it('does that', async () => {
  const wrapper = factory({ propB: 'valueB' });
  wrapper.findComponent(TestComponentB).trigger('click');
  await Vue.nextTick();
});

// ✅ Good
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';

const factory = ({ propA = 'default' }) => {
  const wrapper = mount(TestComponentA, {
    propsData: {
      propA
    },
    components: { TestComponentB }
  });

  async function toggleComponentB() {
    await wrapper.findComponent(TestComponentB).trigger('click');
  }

  return {
    wrapper,
    propA,
    toggleComponentB
  };
};

it('does this', async () => {
  const { wrapper, toggleComponentB, propA } = factory();
  await toggleComponentB();
});

it('does that', async () => {
  const { wrapper, toggleComponentB } = factory({ propA: 'valueB' });
  await toggleComponentB();
});
  • Remember to give proper names to the API interface to provide information on what it does instead of how it does it. You must define interfaces for the API inputs, and the output to keep the API clearer.
// ✅ Good
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';

const factory = ({ propA = 'default', listData }: FactoryOptions): FactoryAPI => {
  const wrapper = mount(TestComponentA, {
    data() {
      list: listData;
    },
    propsData: {
      propA
    },
    components: { TestComponentB }
  });

  async function toggleComponentB() {
    await wrapper.findComponent(TestComponentB).trigger('click');
  }

  return {
    wrapper,
    propA,
    listData,
    toggleComponentB
  };
};

interface FactoryOptions {
  /** PropA passed to the TestComponentA. */
  propA: string;
  /** Data list of the TestComponentA. */
  listData: string[];
}

interface FactoryAPI {
  /** Vue Test Utils Wrapper. */
  wrapper: Wrapper<Vue>;
  /** Current propA passed to the TestComponentA. */
  propA: string;
  /** Current list data of the TestComponentA. */
  listData: string[];
  /** It toggles the TestComponentB. */
  toggleComponentB: () => Promise<void>;
}
  • Sometimes it's easier to create a wrapper component in which you can import and render your component or components, particularly when you use slots, defining the template. It's preferable to test the component isolated without this wrapper. Notice that you can also include this within a factory function to override the template as another property.
// ✅ Good
import TestComponentA from './test-component-a.vue';
import TestComponentB from './test-component-b.vue';

it('does this', () => {
  const wrapper = mount(
    {
      components: { TestComponentA, TestComponentB },
      props: ['propA'],
      template: `<div>
                   <TestComponentA :propA='propA' />
                   <TestComponentB />
                 </div>
                `
    },
    {
      propsData: {
        propA
      }
    }
  );
});