Search code examples
unit-testingvue.jsvuejs2vue-test-utils

Vue.js unit testing ErrorBoundary


I've built simple ErrorBoundary component for my project in Vue.js and I'm struggling to write unit test for it. Component's code below:

<template>
  <div class="overvue-error-boundary">
    <slot v-if="!error" />
    <div class="error-message" v-else>Something went horribly wrong here.</div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      error: false
    }
  },
  errorCaptured (error, vm, info) {
    this.error = true;
  }
}
</script>

I've created an ErrorThrowingComponent that throws an error on created() lifecycle hook so I can test ErrorBoundary:

const ErrorThrowingComponent = Vue.component('error-throwing-component', {
  created() {
    throw new Error(`Generic error`);
  },
  render (h) {
    return h('div', 'lorem ipsum')
  }
});

describe('when component in slot throws an error', () => {
  it('renders div.error-message', () => {
  // this is when error is when 'Generic error' is thrown by ErrorThrowingComponent
  const wrapper = shallowMount(OvervueErrorBoundary, {
    slots: {
      default: ErrorThrowingComponent
    }});
    // below code is not executed
    expect(wrapper.contains(ErrorThrowingComponent)).to.be.false;
    expect(wrapper.contains('div.error-message')).to.be.true;
  });
});

The problem is that ErrorThrowingComponent throws an error when I'm trying to actually mount it (thus failing entire test). Is there any way I can prevent this from happening?

EDIT: What I'm trying to achieve is to actually mount the ErrorThrowing component in a default slot of ErrorBoundary component to assert if ErrorBoundary will render error message and not the slot. This is way I created the ErrorThrowingComponent in the first place. But I cannot assert ErrorBoundary's behavior, because I get an error when trying to create a wraper.


Solution

  • For anyone comming here with a similar problem: I've raised this on Vue Land's #vue-testing channel on Discord, and they suggested to move entire error-handling logic to a function which will be called from the errorCaptured() hook, and then just test this function. This approach seems sensible to me, so I decided to post it here.

    Refactored ErrorBoundary component:

    <template>
      <div class="error-boundary">
        <slot v-if="!error" />
        <div class="error-message" v-else>Something went horribly wrong here. Error: {{ error.message }}</div>
      </div>
    </template>
    <script>
    export default {
      data () {
        return {
          error: null
        }
      },
      methods: {
        interceptError(error) {
          this.error = error;
        }
      },
      errorCaptured (error, vm, info) {
        this.interceptError(error);
      }
    }
    </script>
    

    Unit test using vue-test-utils:

    describe('when interceptError method is called', () => {
      it('renders div.error-message', () => {
        const wrapper = shallowMount(OvervueErrorBoundary);
        wrapper.vm.interceptError(new Error('Generic error'));
        expect(wrapper.contains('div.error-message')).to.be.true;
      });
    });