0

I have a component written in Vue 3 and script setup. Now I am trying to write a unit test for it, where I am instantiating a spy to check whether a function has been called.

I read somewhere that functions in components are closed by default, so I have to add them to the defineExpose function. I did that.

Now when I run my test, for some it's still not seen that the function is called. Does anyone know how this works?

Below is my component:

<script setup lang="ts">
import Modal from '../modal/Modal.vue'
import Button from '../button/Button.vue';
import Select from '../select/Select.vue';
import {ref} from '@vue/reactivity';
import {useI18n} from 'vue-i18n';
import DocumentDiscardModalTypes from './DocumentDiscardModal.types';
import {SelectOption} from '../../../types/utility.types';

const {t} = useI18n();

const emit = defineEmits(['model-closed', 'confirmed']);

const props = defineProps<DocumentDiscardModalTypes>();

const isDiscardDisabled = ref(true);
const selectedOption = ref({
  id: '0',
  label: t('select_an_option'),
});

function resetSelectedOption() {
  selectedOption.value = {
    id: '0',
    label: t('select_an_option'),
  };
  emit('model-closed');
}

function closeConfirmationModal() {
  emit('model-closed');
}

function handleDiscard() {
  emit('confirmed', selectedOption.value.label);
  emit('model-closed');
}

function handleChange(e: SelectOption) {
  selectedOption.value = e;
  isDiscardDisabled.value = false;
}

function getOptions() {
  return [
    {
      id: '0',
      label: t('select_an_option'),
      disabled: true,
    },
    {
      label: t('wrong_document'),
      id: '1',
    },
    {
      label: t('quality_issue'),
      id: '2',
    },
    {
      label: t('other'),
      id: '3',
    },
  ];
}

defineExpose({resetSelectedOption, handleDiscard, getOptions, closeConfirmationModal})
</script>

<template>
  <Modal
      class="discard-aab-modal"
      modal-size="medium"
      :id="id"
      @modal-close="resetSelectedOption"
      :isOpen="isOpen"
      :no-scrolling="true"
  >
    <span slot="modal-title">{{ t('discard_document') }}</span>
    <span slot="modal-content">
        <p class="modal-text">
          {{ t('discard_document_argument') }}
        </p>
        <Select
            id="status-select"
            :options="getOptions()"
            @selected="handleChange"
            :value="selectedOption"/>
      </span>
    <span slot="modal-footer" class="modal-footer">
        <Button
            id="cancel-button"
            @clicked="closeConfirmationModal"
            style-type="secondary"
            :label="t('cancel')"
            :disabled="false"/>
        <Button
            id="discard-button"
            @clicked="handleDiscard"
            style-type="primary"
            :label="t('discard')"
            :disabled="isDiscardDisabled"/>
      </span>
  </Modal>
</template>

<style lang="scss" scoped>
@import 'DocumentDiscardModal.styles';
</style>

And this is my unit test:

describe('DocumentDiscardModal.vue', () => {
    const documentDiscardedModelWrapper = mount(DocumentDiscardModel, {
        propsData: {
            id: 'document-discard-modal',
            isOpen: true,
        },
    });

    const getOptionsSpy = vi.spyOn(documentDiscardedModelWrapper.vm, 'getOptions');

    test('getOptions should have been called', () => {
        expect(getOptionsSpy).toHaveBeenCalled();
    });
}
4
  • 1
    It's called on internal instance in a template so mocking it on vm won't do anything. You couldn't mock it at all if it were called in a script instead of template. It's more straightforward to stick test the behaviour instead of implementation with composition api Commented Jul 4, 2023 at 14:05
  • Hmm, do you have an example for me? I don't fully understand what you mean. Commented Jul 4, 2023 at 14:09
  • 1
    See test-utils.vuejs.org/guide/essentials/… Commented Jul 4, 2023 at 14:16
  • @EstusFlask, I rewrote my tests with your suggestion in mind. It looks way cleaner. Thanks! Commented Jul 5, 2023 at 6:40

1 Answer 1

0

Unlike with Jest, where defineExpose coöperates with spies to let you mock setup functions, I've found that following the Vue Test Utils guide on extracting functions is the safest route. In particular:

  • Mocking a function with const m = vi.spyOn(wrapper.vm, 'f') succeeds, but the mock implementation of f is not called during the test if f is a function in the setup script.
  • Mocking functions that f calls, like vi.spyOn(library, 'g'), works: those mock implementations are called. This may require writing import * as library from 'library', since spyOn expects at least 2 arguments. Here, the library can be an external dependency or an internal module.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.