I have a question about unit testing (Jest) within a vue app - I guess it is rather an architectural question, but maybe specific code examples could also help me out.
I have sketched out a more or less complex setup of vue components from my app. They are still simplified but I guess they should help to describe the situation.
(PDF Attachment for those preferring non-pixel data :)
I have a Wrapper which has three child components:
- Search (Input field to search on Map)
- List (List of stores, represented also as markers on map)
- Map (Google Map itself)
I would really like to write some unit tests for this, but it seems so super tedious to setup those tests, so that I doubt that this is the way to do it.
It starts with the way I load the google maps API: The wrapper loads it asynchronously, sets API key etc. As soon as the google map API is loaded it passes it down to the children.
Of course the map only really starts to load, create and display markers, as soon as google
is present and I can use new this.google.maps.Map()
for example.
Even if I would like to test some methods of the Map component, I have to provide an actual google object, because otherwise the whole init process in mount()
fails. It can't make a map.
So do I have to load the actual google's map API in my tests? Or could I somehow skip mounting the map but still test a few isolated methods?
Further you can see that my Map watches a getter from the vuex store: Whenever the searchCoordinates
set by Search change in my vuex store, I want to execute some functions (for example center my map to the new search coordinates or remove a current location marker if the search did not return valid searchCoordinates).
How could I somehow break down the interlinking between these components, but still write good and helpful tests, which help me to guarantee that the app does what it is supposed to do?
I know that I can use a vuex store within my tests (Medium article about vuex testing). But still I feel like the whole app is so much inter-connected that doing actual unit testing – testing one small piece at a time – is impossible. It seems like that I would have to mount not only Map alone, but also the other components and a vuex store with actual data, to really write bulletproof tests.
A few other examples, where I struggle:
- In my
mounted()
I subscribe to an action withthis.$store.subscribeAction((action) => {...
- of course $store is undefined in my tests. Should I mock it? - As I said I am watching a getter: This throws errors like
TypeError: Cannot use 'in' operator to search for 'searchCoordinates' in undefined
. Would I have to mock all the getters as well? If so: what remains to be tested? If I mock (~=leave away) all the vuex stuff, it seems to me, that testing becomes useless...
But maybe I am mistaken here. Also I am quite new to unit testing, so any hints are very welcome. Thank you very much in advance. I hope somebody has some valuable input. All those examples (even in books like Vue.js - up and running) are always broken down to very simple and understandable examples, but on the other hand don't really reflect real-life apps...
Cheers
----- Edit:
I got a needs more focus
vote by someone.
So I try to sum up simple and small questions:
Summary of Questions
- Does somebody have a general input on how to test larger and more complex vue applications?
- Is there a way to test a component with a google map (or a component that is relying heavily on any third party code)
- Is mocking getters, store and actions a good idea in this case? (I have my doubts there).
- For testing watched properties from the vuex store, I read in this thread that
mapState
should be mocked. (in my case probably my getters). Does this make sense to mocke those getters in my case? - Is it best practice to simplify as many things as possible in an tested component (for example, I started to override some methods, which failed (the one including google map initiation) to just get my test up and running:
const initMapMock = jest.fn();
const initMarkersMock = jest.fn();
...
const wrapper = shallowMount(Map, {
methods: {
initMap: initMapMock, // replace initMap(), which needs google map API
initMarkers: initMarkersMock,
},
...
Does that make sense?