Skip to content

Testing Vue Composables with Jest

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

In this post, we will take a look at how to test Vue composables with Jest.

What Are Composables?

When developing large scale frontend applications, we reuse logic for repetitive tasks. With composables, we are able to resuse logic in a stateful manner with the help of the composition API.

Testing

For this example, we create a composable to handle changes in screen size. Depending on how complex your composable is, you can simply test the reactivity directly or load it into a wrapper component, and then test the wrapper component.

import { computed, ref } from 'vue';

const defaultBreakpoints = {
		MOBILE: 320,
		TABLET: 720,
		DESKTOP: 1000,
		DESKTOP_WIDE: 1200,
		DESKTOP_EXTRAWIDE: 2000
}

// return the largest breakpoint that matches the current screen width
export function useBreakpoints( breakpoints ) {
	breakpoints = breakpoints || defaultBreakpoints;
	const windowWidth = ref( window.innerWidth );

	const onWidthChange = () => {
		windowWidth.value = window.innerWidth;
	};
	Vue.onMounted( () => {
		window.addEventListener( 'resize', onWidthChange );
	} );
	Vue.onUnmounted( () => {
		window.removeEventListener( 'resize', onWidthChange );
	} );

	const currentBreakpoint = Vue.computed( () => {
		for ( var breakpoint in breakpoints ) {
			if ( windowWidth.value >= breakpoints[ breakpoint ] ) {
				return breakpoint;
			}
		}

    return null
	} );

	return {
    current: currentBreakpoint
  };
};

Let's write the following tests for this composable.

  • check currect value of screen size
  • check currect value of screen size after resize event is fired

To test that the resize event changes the current value when fired, we need to load our composable in a component.

import { createApp } from 'vue';

function mockLoadComposableInApp( composable ) {
	let result;
	const app = createApp( {
		setup() {
			result = composable();
			// suppress missing template warning
			return () => {};
		}
	} );
	app.mount( document.createElement( 'div' ) );
	return [ result, app ];
}

describe( 'useBreakpoints', () => {
	describe( 'when innerWidth is 1200 pixel', () => {
		beforeEach( () => {
			global.innerWidth = 1200;
		} );

		it( 'return current value as "DESKTOP_WIDE"', () => {
			const result = useBreakpoints();
			expect( result.current.value ).toBe( 'DESKTOP_WIDE' );
		} );

    it( 'update current value when resize event is triggered', () => {

			const result = mockLoadComposableInApp( () => useBreakpoints() )[ 0 ];

			global.innerWidth = 800;
			global.dispatchEvent( new Event( 'resize' ) );

			expect( result.current.value ).toBe( 'TABLET );
		} );
  } );
})

From the implementation we completed above, the core logic of our breakpoint method is in an external function called "composable", and we can simply reuse it across our frontend code. Any manipulation to the state of a composable should be done directly within the composable to avoid bugs and make it easier to read.

I hope this article has been helpful. If you have any questions or run into any trouble, feel free to reach out on Twitter or Github.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co