Search code examples
javascriptvue.jssinonchaiavoriaz

Mounting a vue instance with avoriaz, but cannot sinon spy on imported function


I have the following component script (some irrelevant bits removed):

import api from '@/lib/api';

export default {
  methods: {
    upload (formData) {
      api.uploadFile(formData).then(response => {
        this.$emit('input', response.data);
      });
    }
  }
};

And I have the following test, which uses avoriaz to mount the Vue instance:

import { mount } from 'avoriaz';
import { expect } from 'chai';
import sinon from 'sinon';
import UploadForm from '@/components/UploadForm';

describe('upload', () => {
  it('passes form data to api.uploadFile', () => {
    const testFormData = { test: 'test' };
    const api = {
      uploadFile: sinon.spy()
    };
    const wrapper = mount(UploadForm);
    wrapper.vm.api = api;
    wrapper.vm.upload(testFormData);
    expect(api.uploadFile.called).to.equal(true);
  });
});

My sinon spy is never called, and I've tried a couple different variations on the above. What is the best way to spy on an imported function like this? Or am I conceptually approaching this the wrong way?


Solution

  • Problem

    You need to stub the api dependency, which is a dependency of the file. This can't be done through the vue instance, since api is not a part of the vue component.

    You need to stub the file dependency.

    Solution

    One method to do this is to use inject-loader.

    Steps

    Install inject-loader

    npm install --save-dev inject-loader
    

    At the top of your file, import UploadForm with inject-loader and vue-loader:

    import UploadFormFactory from '!!vue-loader?inject!@/components/UploadForm';
    

    This is a factory function that returns UploadForm with dependencies stubbed.

    Now, in your test you need to call UploadFormFactory with the dependency you want stubbed:

    const api = {
      uploadFile: sinon.spy()
    };
    
    const UploadForm = UploadFormFactory({
      '@/lib/api': api
    })
    

    So your test file will look like:

    import { mount } from 'avoriaz';
    import { expect } from 'chai';
    import sinon from 'sinon';
    import UploadFormFactory from '!!vue-loader?inject!@/components/UploadForm';
    
    describe('upload', () => {
      it('passes form data to api.uploadFile', () => {
        const api = {
          uploadFile: sinon.spy()
        };
    
        const UploadForm = UploadFormFactory({
          '@/lib/api': api
        })
        const testFormData = { test: 'test' };
        const api = {
          uploadFile: sinon.spy()
        };
        const wrapper = mount(UploadForm);
        wrapper.vm.upload(testFormData);
        expect(api.uploadFile.called).to.equal(true);
      });
    });
    

    More info

    I've written a tutorial with more detail here - https://www.coding123.org/stub-dependencies-vue-unit-tests/