Streamline your Product workflow: a guide to Raycast, Perplexity AI, and Arc Browser
Tired of inefficient workflows? Learn how to supercharge your productivity with these essential tools for product teams.

Bruno Teixeira
Head of Product

This post was published in 2017, and some information may now be outdated.
When building a web-app in Vue or any other JS Framework, it’s important to make sure Vue components work as intended throughout the many iterations a project can have. Vue.js allows a very easy and ready to go testing setup with Vue Webpack Template, which already includes a boilerplate for unit tests (using Karma and Mocha) and E2E tests (using Nightwatch). The purpose of this article is to provide different unit test examples on various domains using Karma and Mocha. But first, it’s important to understand some basic JS testing keywords.
[Card Banner][Card Banner][Card Banner]A component has many structures that should be tested:
Lifecycle hooks: For example, test if a function is called when the component is mounted, updated, …
Methods: Test if the function’s return is the expected or if the changes on data were correctly made.
Watchers: When changing a prop or data value, check if the watcher is invoked.
Computed properties: Check if the computed property is returning the intended value.
Components can have multiple functions that use each other as a dependency and this can turn into some complex behaviors. So, it’s important to:
Try to make small, easy to test functions (too many dependencies or complex functions make tests harder to write — and the code harder to read as well).
A function should return a value or change a field on the component’s data in order to be easier to test.
Control your test environment. Focus on testing a single unit of code, mocking the environment or dependencies invocations. Using shallow instead of mount helps to control how the component’s instance renders. When a component is connected to store (either by mapActions, mapState or mapGetters) it’s really important to mock the store and pass it as a global parameter to the component’s instance.
When a component instance is created, a wrapper around the component instance (also named vm) is created and it provides a great API to edit props, data, and many other properties.
Be aware that avoriaz syntax (which is very similar to vue-test-utils syntax) is used in the following tests.
A Vue component has multiple lifecycle hooks and to test those, actions that trigger them must be done (create the instance to test beforeMount and mounted while destroying it will trigger the beforeDestroy and destroyed).
Using this component:
<br>&lt; template &gt; <br><br>&nbsp;&lt; div &gt; &lt; /div &gt; <br><br>&lt; template &gt;<br><br>&lt; script &gt;<br><br>&nbsp;export default {<br><br> &nbsp;&nbsp; // == Lifecycle<br><br> &nbsp;&nbsp; mounted () {<br><br> &nbsp;&nbsp;&nbsp; window.addEventListener('scroll', this.handleScroll)<br><br> &nbsp;&nbsp;},<br><br> &nbsp;&nbsp;destroyed () {<br><br> &nbsp;&nbsp;&nbsp; window.removeEventListener('scroll', this.handleScroll)<br><br> &nbsp;&nbsp;},<br><br>&nbsp;&nbsp;// == Methods<br><br> &nbsp;&nbsp; methods: {<br><br> &nbsp;&nbsp;&nbsp; handleScroll () {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; console.log('handleScroll')<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return<br><br> &nbsp;&nbsp;&nbsp; }<br><br> &nbsp;&nbsp; }<br><br>&nbsp; }<br><br>&lt; /script &gt;<br><br></p><p></p><p id="">To check if <em id="">mounted</em> and <em id="">destroyed</em> hooks work as intended, an instance of this component should be created and destroyed. Since <em id="">stub</em> can be applied not only to the component’s instance (and therefore the component’s methods) but also to the <em id="">window</em>, it’s possible to verify if events are being handled as well.</p><p id="">On the test below it’s possible to see how the lifecycle hooks are tested:</p><p id="">-- CODE language-js line-numbers --<br>describe('Basic component', () => { <br><br> &nbsp; describe('Lifecycle', () => {<br><br> &nbsp;&nbsp; it('Mounted', done => {<br><br> &nbsp;&nbsp;&nbsp; const handleScrollStub = sinon.stub(BasicComponent.methods, 'handleScroll')<br><br> &nbsp;&nbsp;&nbsp; const addEventStub = sinon.stub(window, 'addEventListener')<br><br>&nbsp;&nbsp;&nbsp;const wrapper = mount(BasicComponent)<br><br>&nbsp;&nbsp;&nbsp;expect(addEventStub).to.be.calledWith('scroll', wrapper.vm.handleScroll)<br><br> &nbsp;&nbsp;&nbsp; handleScrollStub.restore()<br><br> &nbsp;&nbsp;&nbsp; addEventStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br>&nbsp;&nbsp; it('Destroyed', done => {<br><br> &nbsp;&nbsp;&nbsp; const wrapper = mount(BasicComponent)<br><br> &nbsp;&nbsp;&nbsp; const handleScrollStub = sinon.stub(wrapper.vm, 'handleScroll')<br><br> &nbsp;&nbsp;&nbsp; const removeEventStub = sinon.stub(window, 'removeEventListener')<br><br> &nbsp;&nbsp;&nbsp; wrapper.destroy()<br><br>&nbsp;&nbsp;&nbsp; expect(removeEventStub).to.be.calledWith('scroll', wrapper.vm.handleScroll)<br> <br>&nbsp;&nbsp;&nbsp;handleScrollStub.restore()<br><br> &nbsp;&nbsp;&nbsp; removeEventStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br>&nbsp; })<br><br>})<br></p><p id=""></p><p id="">Creating <em id="">spies</em> or <em id="">stubs</em> on the component (BasicComponent — as you see in the Mounted example) instead of creating them on the instance (<em id="">wrapper.vm</em>) is useful when you want to mock / spy on a dependency that will be invoked on the instance mount lifecycles (like <em id="">beforeMount </em>or <em id="">mounted</em>).</p><h3 id="">Testing Methods</h3><p id="">Methods should always have a clear way of being tested, either by returning a value / Promise, changing a component’s <em id="">data </em>or controlling other <em id="">functions</em> invocations. Thinking of how a function will be tested will very probably improve the code’s quality and maintainability.</p><p id="">Many times <em id="">functions </em>behavior changes depending on a parameter value or other <em id="">function’s </em>invocation. The context should be mocked (probably using <em id="">stubs)</em> to test all use cases.</p><p id="">-- CODE language-js line-numbers --<br>&lt; template &gt; <br><br>&nbsp; &lt; div &gt; &lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>export default {<br><br> &nbsp; ...<br><br> &nbsp; data () {<br><br> &nbsp; &nbsp; return {<br><br> &nbsp; &nbsp; &nbsp; currentObjects: [],<br><br> &nbsp; &nbsp; &nbsp; hasError: false<br><br> &nbsp; &nbsp; }<br><br> &nbsp; },<br><br> &nbsp; methods: {<br><br> &nbsp; &nbsp; getObjects () {<br><br> &nbsp; &nbsp; &nbsp; ...<br><br> &nbsp; &nbsp; },<br><br> &nbsp; &nbsp; setObjects () {<br><br> &nbsp; &nbsp; &nbsp; const objectsOnStore = this.getObjects()<br><br> &nbsp; &nbsp; &nbsp; if(objectsOnStore) {<br><br> &nbsp; &nbsp; &nbsp; &nbsp; this.currentObjects = objectsOnStore<br><br> &nbsp; &nbsp; &nbsp; &nbsp; return true<br><br> &nbsp; &nbsp; &nbsp; } else {<br><br> &nbsp; &nbsp; &nbsp; &nbsp; this.hasError = true<br><br> &nbsp; &nbsp; &nbsp; &nbsp; return false<br><br> &nbsp; &nbsp; &nbsp; }<br><br> &nbsp; &nbsp; }<br><br> &nbsp; }<br><br>}<br><br>&lt; /script &gt; <br></p><p></p><p id="">In the above scenario, <em id="">setObjectsfunction</em> will have a behavior depending on <em id="">getObjects </em>result. In order to test both use cases, the <em id="">return</em> value of <em id="">getObjects </em>invocation should be controlled as seen in this test:</p><p id="">-- CODE language-js line-numbers -- <br>describe('Objects component', () => { <br><br> &nbsp;describe('Methods', () => {<br><br> &nbsp;&nbsp; it('getObjects - should return true and set the data.currentObjects if getObjects retrieves an array of objects', done => {<br><br> &nbsp;&nbsp;&nbsp; const mockedObjects = [<br><br> &nbsp;&nbsp;&nbsp;&nbsp;{ id: 1 },<br><br> &nbsp;&nbsp;&nbsp;&nbsp; { id: 2 }<br><br> &nbsp;&nbsp;&nbsp;]<br>const wrapper = mount(ObjectsComponent) <br><br> &nbsp;&nbsp;&nbsp; const getObjectsStub = sinon.stub(wrapper.vm, 'getObjects').returns(mockedObjects)<br><br>&nbsp;&nbsp;&nbsp;const result = wrapper.vm.getObjects()<br><br>&nbsp;&nbsp;&nbsp;expect(result).to.be.true<br><br>&nbsp;&nbsp;&nbsp;expect(wrapper.data().currentObjects).to.be.deep.equal(mockedObjects)<br><br>&nbsp;&nbsp;&nbsp;getObjectsStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br>&nbsp;&nbsp;it('getObjects - should return false and set the data.hasError to true if getObjects retrieves an empty array', done => {<br><br> &nbsp;&nbsp;&nbsp; const mockedObjects = []<br><br> &nbsp;&nbsp;&nbsp; const wrapper = mount(ObjectsComponent) <br><br> &nbsp;&nbsp;&nbsp;const getObjectsStub = sinon.stub(wrapper.vm, 'getObjects').returns(mockedObjects)<br><br> &nbsp;&nbsp;&nbsp;const result = wrapper.vm.getObjects()<br><br> &nbsp;&nbsp;&nbsp; expect(result).to.be.false<br><br> &nbsp;&nbsp;&nbsp; expect(wrapper.data().hasError).to.be.true<br><br> &nbsp;&nbsp;&nbsp; getObjectsStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br> &nbsp; })<br><br>})<br></p><p id=""></p><p id="">When using a <em id="">function</em> that depends on an API call, an approach to take is to use a <em id="">Promise</em>. In this scenarios, the best way to proceed is to <em id="">return </em>the <em id="">Promise</em>.</p><p id="">The following component is connected to store. The store should be completely mocked in order to control the test’s context and avoid any kind of errors.</p><p id="">-- CODE language-js line-numbers -- &lt; template &gt; <br><br>&nbsp; &lt; div &gt; &lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>import { GET_OBJECTS } from 'services/constants/action-types'<br><br>export default {<br><br> &nbsp; ...<br><br> &nbsp;data () {<br><br> &nbsp;&nbsp; return {<br><br> &nbsp;&nbsp;&nbsp; currentObjects: []<br><br> &nbsp;&nbsp; }<br><br> &nbsp; },<br><br> &nbsp; methods: {<br><br> &nbsp;&nbsp; ...mapActions('objects', {<br><br> &nbsp;&nbsp;&nbsp; getObjectsAction: GET_OBJECTS<br><br> &nbsp;&nbsp; }),<br><br> &nbsp; &nbsp; setObjects () {<br><br> &nbsp;&nbsp;&nbsp;return this.getObjectsAction()<br><br> &nbsp;&nbsp;&nbsp;&nbsp; .then((response) => {<br><br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.currentObjects = response<br><br> &nbsp;&nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp; }<br><br>&nbsp;}<br><br>&lt; /script &gt; <br></p><p id=""></p><p id="">Returning the <em id="">Promise</em> allows the API call to be stubbed and this way, it triggers the <em id="">Promise’s resolve. </em>All the assertions should run on the callback.</p><p id="">Considering that <em id="">getObjectsAction</em> returns a <em id="">Promise</em> (that will perform the API request on the action), a good way to test this <em id="">function</em> would be:</p><p id="">--CODE language-js line-numbers -- <br>import { GET_OBJECTS } from 'services/constants/action-types'<br><br>describe('Objects component', () => {<br><br>&nbsp; let store<br><br> &nbsp;let state<br><br> &nbsp;let actions<br><br>beforeEach(() => {<br><br> &nbsp; actions = {}<br><br> &nbsp; actions[GET_OBJECTS] = sinon.stub()<br><br>&nbsp;state = {<br><br> &nbsp;&nbsp; modules: {<br><br> &nbsp;&nbsp; // mock state, with actions and getters if any<br><br> &nbsp;&nbsp; objects: {<br><br> &nbsp;&nbsp;&nbsp; namespaced: true,<br><br> &nbsp;&nbsp;&nbsp; state: {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; objs: []<br><br> &nbsp;&nbsp;&nbsp; },<br><br> &nbsp;&nbsp;&nbsp; actions<br><br> &nbsp;&nbsp; }<br><br> &nbsp; }<br><br> &nbsp; }<br><br>&nbsp;store = new Vuex.Store(state)<br><br> })<br><br> describe('Methods', () => {<br><br> &nbsp; it('getObjects - should set data.currentObjects with response returned from getObjects', done => {<br><br> &nbsp;&nbsp; const mockedObjects = [<br><br> &nbsp;&nbsp;&nbsp; { id: 1 },<br><br> &nbsp;&nbsp;&nbsp; { id: 2 }<br><br> &nbsp;&nbsp; ]<br><br>&nbsp;&nbsp;const wrapper = shallow(ObjectsComponent, {<br><br> &nbsp;&nbsp;&nbsp; store<br><br> &nbsp;&nbsp; })<br><br>&nbsp;&nbsp;const getObjectsStub = sinon.stub(wrapper.vm, 'getObjects').returns(mockedObjects)<br><br>&nbsp;&nbsp;wrapper.vm.getObjects()<br><br> &nbsp;&nbsp;&nbsp; .then((result) => {<br><br> &nbsp;&nbsp;&nbsp; expect(result).to.be.true<br><br> &nbsp;&nbsp;&nbsp; expect(wrapper.data().currentObjects).to.be.deep.equal(mockedObjects)<br><br>&nbsp;&nbsp;&nbsp;getObjectsStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br> &nbsp; })<br><br> &nbsp; })<br><br>})</p><p id=""></p><p id="">This approach (returning a <em id="">Promise</em> or a chain of <em id="">Promises</em>) is very useful in scenarios where requests need to be mocked to avoid <em id="">timeout </em>errors and allowing to test how a <em id="">function </em>will perform after the <em id="">Promise’s resolve. </em>Instead of resolving the <em id="">Promise, reject</em> it will also trigger the <em id="">catch </em>scenario on the test.<br></p><h3 id="">Testing Watchers</h3><p id="">A <em id="">watcher</em> is used to react to data changes and it can be applied to a component’s <em id="">props</em> or <em id="">data. </em>To trigger <em id="">watchers</em> on the test, <a href="https://vue-test-utils.vuejs.org/en/api/wrapper/setData.html" target="_blank" id=""><em id="">setData</em></a><em id=""> </em>or <a href="https://vue-test-utils.vuejs.org/en/api/wrapper/setProps.html" target="_blank" id=""><em id="">setProps</em></a> methods can be used.</p><p id="">-- CODE language-js line-numbers -- <br>&lt; template &gt; <br><br>&nbsp; &lt; div &gt; &lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>export default { <br><br> &nbsp; props: {<br><br> &nbsp;&nbsp; propId: {<br><br> &nbsp;&nbsp;&nbsp; type: Number,<br><br> &nbsp;&nbsp;&nbsp; required: true<br><br> &nbsp;&nbsp; }<br><br>&nbsp; },<br><br> &nbsp; methods: {<br><br> &nbsp;&nbsp; consoleLogValue(value) {<br><br> &nbsp;&nbsp;&nbsp; console.log(value)<br><br> &nbsp;&nbsp; }<br><br> &nbsp; },<br><br>&nbsp; watch: {<br><br> &nbsp;&nbsp; propId (newVal) {<br><br> &nbsp;&nbsp;&nbsp; this.consoleLogValue(newVal)<br><br> &nbsp;&nbsp; }<br><br> &nbsp;},<br><br>}<br><br>&lt; /script &gt; <br></p><p id=""></p><p id="">Changing <em id="">propId</em> will trigger the <em id="">watcher</em>.</p><p id="">This test will only check if <em id="">consoleLogValue </em>is being invoked with the correct parameter using <em id="">calledWith</em> as there is no intention to verify how <em id="">consoleLogValue </em>behaves. That should be done in another test. For this reason, a <em id="">stub </em>is applied to <em id="">consoleLogValue.</em></p><p id="">-- CODE language-js line-numbers -- <br>describe('Watcher component', () => { <br><br> &nbsp; let props<br><br> &nbsp; beforeEach(() => {<br><br> &nbsp;&nbsp; props = {<br><br> &nbsp;&nbsp;&nbsp; propId: 1<br><br> &nbsp;&nbsp; }<br><br> &nbsp; })<br><br> &nbsp; describe('Watcher', () => {<br><br> &nbsp;&nbsp; it('propId', done => { <br><br> &nbsp;&nbsp;&nbsp; const wrapper = shallow(WatcherComponent, {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; propsData: props<br><br> &nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp;&nbsp;const consoleLogValueStub = sinon.stub(wrapper.vm, 'consoleLogValue')<br><br>&nbsp;&nbsp;&nbsp;wrapper.setProps({<br><br> &nbsp;&nbsp;&nbsp;&nbsp; propId: 2<br><br> &nbsp;&nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp;&nbsp;expect(consoleLogValueStub).to.be.calledWith(2)<br><br> &nbsp;&nbsp;&nbsp; consoleLogValueStub.restore()<br><br> &nbsp;&nbsp;&nbsp; done() <br><br> &nbsp;&nbsp; })<br><br> &nbsp; })<br><br>})<br></p><h3 id="">Testing computed properties</h3><p id="">It’s usual to use computed properties to retrieve a value depending on one or multiple props. Computed properties usually represent simple operations that shouldn’t be placed on the <em id="">template</em> to be easier to maintain.</p><p id="">-- CODE language-js line-numbers -- <br>&lt; template &gt; <br><br>&nbsp; &lt; div :class="themeDiv" &gt; &lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>export default { <br><br> &nbsp;props: {<br><br> &nbsp;&nbsp; plan: {<br><br> &nbsp;&nbsp;&nbsp; required: true,<br><br> &nbsp;&nbsp;&nbsp; type: String<br><br> &nbsp;&nbsp; }<br><br> &nbsp;},<br><br> &nbsp;computed: {<br><br> &nbsp;&nbsp; themeDiv: function () {<br><br> &nbsp;&nbsp;&nbsp; if (this.plan === 'pro') {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return 'blue'<br><br> &nbsp;&nbsp;&nbsp; } else if (this.plan === 'trial') {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return 'soft-blue'<br><br> &nbsp;&nbsp;&nbsp; } else {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return 'orange'<br><br> &nbsp;&nbsp;&nbsp; }<br><br> &nbsp;&nbsp; }<br><br> &nbsp; }<br><br>}<br><br>&lt; /script &gt; <br></p><p id=""></p><p id="">To access computed properties, we can use the component instance directly (<em id="">wrapper.vm.computedProperty</em>). That said, <em id="">setProps</em> can be used to test all the branches:</p><p id=""><p>-- CODE language-js line-numbers -- <br>describe('Computed component', () => { <br><br> &nbsp;let props<br><br><br> &nbsp; beforeEach(() => {<br><br> &nbsp;&nbsp; props = {<br><br> &nbsp;&nbsp;&nbsp; plan: 'pro'<br><br> &nbsp;&nbsp; }<br><br> &nbsp; })<br><br><br> &nbsp; describe('Computed', () => {<br><br> &nbsp;&nbsp; it('themeDiv', done => { <br><br> &nbsp;&nbsp;&nbsp; const wrapper = shallow(ComputedComponent, {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; propsData: props<br><br> &nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp;&nbsp; expect(wrapper.vm.themeDiv).to.equal('blue')<br><br><br> &nbsp;&nbsp;&nbsp; wrapper.setProps({<br><br> &nbsp;&nbsp;&nbsp;&nbsp; theme: 'trial'<br><br> &nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp;&nbsp; expect(wrapper.vm.themeDiv).to.equal('soft-blue')<br><br><br> &nbsp;&nbsp;&nbsp;wrapper.setProps({<br><br> &nbsp;&nbsp;&nbsp;&nbsp; theme: 'standard'<br><br> &nbsp;&nbsp;&nbsp;})<br><br> &nbsp;&nbsp;&nbsp; expect(wrapper.vm.themeDiv).to.equal('orange')<br><br><br> &nbsp;&nbsp;&nbsp;done()<br><br> &nbsp;&nbsp;})<br><br> &nbsp;})<br><br>}) </p></p><h3 id="">Component Tests — Using the Object</h3><p id="">As explained before, Vue components’ attributes are all functions. This mindset will make it easier to approach the tests we only need to know how is the object composed. Other than that we’re just testing a function, a small unit of code that shouldn’t have many dependencies.</p><p id="">Considering the following component:</p><p id=""><p>-- CODE language-js line-numbers -- <br>&lt; template &gt; <br><br>&nbsp; &lt; div :class="getClasses" &gt; <br><br>&nbsp;Another component, another test, random probability<br><br>&lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>export default {<br><br> &nbsp; props: {<br><br> &nbsp;&nbsp; classModifiers: {<br><br> &nbsp;&nbsp;&nbsp; type: Array,<br><br> &nbsp;&nbsp;&nbsp; default: () => ['large']<br><br> &nbsp;&nbsp; }<br><br> &nbsp; },<br><br> &nbsp;computed: {<br><br> &nbsp;&nbsp; baseProbability () {<br><br> &nbsp;&nbsp;&nbsp; if (this.classModifiers.includes('large')) {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return .8<br><br> &nbsp;&nbsp;&nbsp; }<br><br><br> &nbsp;&nbsp;&nbsp; return .5<br><br> &nbsp;&nbsp; },<br><br> &nbsp;&nbsp;getClasses () {<br><br> &nbsp;&nbsp;&nbsp; return this.classModifiers.map(function (class) {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; return `probability--${modifier}`<br><br> &nbsp;&nbsp;&nbsp; })<br><br> &nbsp;&nbsp; }<br><br> &nbsp;},<br><br> &nbsp; methods: {<br><br> &nbsp;&nbsp;calcProbability (probMultiplier) {<br><br> &nbsp;&nbsp; &nbsp;return this.baseProbability * probMultiplier<br><br> &nbsp; &nbsp; }<br><br> &nbsp; }<br><br> }<br><br>&lt; /script &gt; <br></p><p id=""></p></p><p id="">In order to test a function that requires values from <em id="">props</em> or from component’s <em id="">data</em> (as the Vue instance will not be created now), a variable must be created with this values and passed to the tested function. On the following tests, this variable will be named ‘context’.</p><p id=""><p>-- CODE language-js line-numbers --<br>// Working with the object here! <br><br>import Probability from '../probability.vue'<br><br><br>describe('Probability component', () => {<br><br> &nbsp;describe('Computed ', () => {<br><br> &nbsp;&nbsp; &nbsp; it('getClasses', done => {<br><br> &nbsp;&nbsp;&nbsp; &nbsp; const context = {<br><br> &nbsp;&nbsp;&nbsp; &nbsp; classModifiers: ['medium', 'blue-theme', 'rounded-corners']<br><br> &nbsp;&nbsp;&nbsp; }<br><br><br><br> &nbsp;&nbsp; &nbsp;expect(Probability.computed.getClasses.call(context))<br><br> &nbsp;&nbsp;&nbsp; &nbsp; .to.be.eql(['probability--medium', 'probability--blue-theme', 'probability--rounded-corners'])<br><br> &nbsp;&nbsp; &nbsp;&nbsp; done()<br><br> &nbsp;&nbsp;&nbsp; })<br><br><br><br> &nbsp;&nbsp; it('baseProbability - should return 0.8 when "large" is on the classModifiers list', done => {<br><br> &nbsp;&nbsp;&nbsp; const context = {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; classModifiers: ['large', 'blue-theme', 'rounded-corners']<br><br> &nbsp;&nbsp;&nbsp; }<br><br><br><br> &nbsp;&nbsp;&nbsp; expect(Probability.computed.baseProbability.call(context)).to.be.eq(.8)<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br><br><br> &nbsp;&nbsp; it('baseProbability - should return 0.5 when "large" is not on the classModifiers list', done => {<br><br> &nbsp;&nbsp;&nbsp; const context = {<br><br> &nbsp;&nbsp;&nbsp;&nbsp; classModifiers: ['medium', 'blue-theme', 'rounded-corners']<br><br> &nbsp;&nbsp;&nbsp; }<br><br><br><br> &nbsp;&nbsp;&nbsp; expect(Probability.computed.baseProbability.call(context)).to.be.eq(.5)<br><br> &nbsp;&nbsp;&nbsp; done()<br><br> &nbsp;&nbsp; })<br><br> &nbsp; })<br><br><br><br>&nbsp; describe('Methods ', () => {<br><br> &nbsp;&nbsp; it('calcProbability', done => {<br><br> &nbsp;&nbsp;&nbsp; const context = {<br><br> &nbsp;&nbsp;&nbsp; baseProbability: .5<br><br> &nbsp;&nbsp; }<br><br><br><br> &nbsp;&nbsp; expect(Loading.methods.calcProbability.call(context, 100)).to.be.eq(50)<br><br> &nbsp;&nbsp; done()<br><br> &nbsp; })<br><br> })<br><br>})<br></p></p><p id=""></p><p id="">Without a Vue instance, invoking functions uses the object’s properties (<em id="">Object.computed</em>, <em id="">Object.methods</em>, etc). The ‘context’ variable allows the tested function to still use the expected values from the Vue instance data structure.</p><p id="">The lifecycles hooks can also be invoked using the object’s properties. Operations like using functions created on <em id="">mapActions</em> can be placed on the ‘context’ variable to avoid mocking the store (one of this approach’s major advantages). Using the following component:</p><p id=""><p>-- CODE language-js line-numbers --<br>&lt; template &gt; <br><br>&nbsp; &lt; div &gt; <br><br>&nbsp;Lifecycle using object example<br><br>&lt; /div &gt; <br><br>&lt; /template &gt; <br><br>&lt; script type="text/javascript" &gt; <br><br>import { mapActions } from 'vuex'<br><br> import { actionTypes } from 'services/constants'<br><br><br><br> export default {<br><br> &nbsp; methods: {<br><br> &nbsp; &nbsp; ...mapActions('app', {<br><br> &nbsp; &nbsp; &nbsp; getInitialConfig: actionTypes.APP_GET_CONFIG<br><br> &nbsp; &nbsp; })<br><br> &nbsp; },<br><br> &nbsp; created () {<br><br> &nbsp; &nbsp; this.getInitialConfig()<br><br> &nbsp; }<br><br> }<br><br>&lt; /script &gt; <br><br></p></p><p id=""></p><p id="">The <em id="">getInitialConfig</em> function will be placed inside the ‘context’ variable and therefore, a spy is created to understand if the function is <em id="">called</em> when the <em id="">created</em> hook is invoked.</p><p id=""><p>-- CODE language-js line-numbers --<br>import Obj from '../Obj.vue'<br><br><br>describe('Obj', () => {<br><br> &nbsp; describe('Lifecycle', () => {<br><br> &nbsp; &nbsp; it('created', done => {<br><br> &nbsp; &nbsp; &nbsp; const context = {<br><br> &nbsp; &nbsp; &nbsp; &nbsp; getInitialConfig: () => {}<br><br> &nbsp; &nbsp; &nbsp; }<br><br><br> &nbsp; &nbsp; &nbsp; const getInitialConfigSpy = sinon.spy(context, 'getInitialConfigSpy')<br><br> &nbsp; &nbsp; &nbsp; Obj.created.call(context)<br><br><br> &nbsp; &nbsp; &nbsp; expect(getInitialConfigSpy).to.be.calledOnce<br><br><br> &nbsp; &nbsp; &nbsp; getInitialConfigSpy.restore()<br><br> &nbsp; &nbsp; &nbsp; done()<br><br> &nbsp; &nbsp; })<br><br> &nbsp; })<br><br>})<br><br></p></p><h3 id="">Conclusion</h3><p id="">Unit testing can sometimes be tricky and if you’re starting, it will surely cause some pain. But in the end, it’s worth it.</p><p id="">Tests bring great advantages to the project and to the developer’s evolution. The developer’s mindset will change to write better, easier to test and more maintainable code using pure functions, following the <em id="">Do One Thing (DOT) </em>rule instead of overloading a unit of code with multiple responsibilities and taking better decisions over the structure and scalability of an application.</p><p id="">This article focused on showing some examples of how to approach various Vue.js testing domains, more like an easy to remind / understand how to approach a certain scenario and to help you all understand what it’s being done when writing a test. Hope it was helpful! </p>Share this article