Search code examples
javascriptunit-testingvue.jsjestjsvue-test-utils

How to mock window.google in a vue.js unit test


I am trying to test a vue component that displays a google map

The google include script element is somewhere in a parent component and the component I am trying to test gets is reference to it globally:

const googleInstance = window.google

My alarm bells rang when I saw it is global, but its the code I have been given and I need to get my coverage higher!

The code in the component gets the instance here:

this.map = new googleInstance.maps.Map(mapElement, this.options)

I get many errors starting with:

TypeError: Cannot read property 'maps' of undefined

I have tried adding googleInstance and google to the mocks parameter when shallow mounting the wrapper

const wrapper = shallowMount(Map, {
  mocks: {
    google: {
      maps: () => {}
    }
  }
})

Neither worked and I get the same response:

TypeError: Cannot read property 'maps' of undefined

I tried:

global.google = {
  maps: () => {}
}

That did not work either

Here is a simplified version of the map component that I am trying to test:

<template>
<div>
  <div refs="mapLayer" :id="mapName" class="mapLayer" />
</div>
</template>
<script>
const googleGlobal = window.google

export default {
  name: 'Map',
  props: {
    name: {
      type: String,
      required: true
    }
  },
  mounted () {
    this.initMap()
  },
  methods: {
    initMap () {
      const mapElement = document.getElementById(this.mapName)
      this.map = new googleGlobal.maps.Map(mapElement)
    }
  }
}
</script>

The code has been refactored and previously the google instance came via the Vuex store and my tests worked

My other thought was to return googleInstance from a separate file, which I could then mock using jest, but ultimately that just moves the problem to another file that would still be untestable

How can I mock the values in my component or how could the code be refactored to be testable?


Solution

  • The problem is that your const googleGlobal = window.google sentence is being executed before you introduce the mock in the test file.

    Because of this, the googleGlobal constant is equal to undefined. A solution for this could be to define a method in your component that returns the global variable google, and obtain the reference by calling this method.

    <script>
    export default {
        name: 'Map',
        props: {
            name: {
                type: String,
                required: true
            }
        },
        mounted () {
            this.initMap()
        },
        methods: {
            getGoogle() {
                return window.google
            },
            initMap () {
                const googleGlobal = this.getGoogle()
                const mapElement = document.getElementById(this.mapName)
                this.map = new googleGlobal.maps.Map(mapElement)
            }
        }
    }
    </script>
    

    Then, in your test file you can mock window.google like:

    window.google = {
        maps: { Map: function() {} }
    }