Mock function in when testing another function in Vue composable

143 Views Asked by At

I have a case with the following:

  1. Composable function useTreeData(), which has many other functions inside
  2. Function that is returned as a result of destructuring the useTreeData, called onNodeChange()
  3. Other functions being called inside which are also part of the same composable such as saveTreeState()

I am writing a test for the function onNodeChange, where I am not concerned about other functions, which are already tested, and I only want to make sure they are properly called. However, when the actual test runs, the functions are not replaced by my mocked spies(via vi.fn()), but the original function is called.

The main composable which is initialized before each test:

const useTreeData = () => {

   function saveTreeState() {
     // does something
   }

   function onNodeChange() {
      // calls other functions
      saveTreeState();
   }

   return {
      saveTreeState,
      onNodeChange
   };
};

I write a test as follows(using vue test utils, vitest). The composable is initialized every time in beforeEach via let treeData = useTreeData();

treeData.currentPanel.value = 1;
treeData.saveTreeState = vi.fn();
treeData.onNodeChange();
expect(treeData.saveTreeState).toHaveBeenCalledOnce();

However this fails, because it is not actually calling the spy, but calling the function inside the composable body.

I've also tried some variants of vi.doMock and mocking the file itself, but with no luck. The import is done from a barrel file containing many composables.

1

There are 1 best solutions below

0
On

The reason why it is not working is that you are assigning your mock function to the instance of that object, but inside of the onNodeChange you are using another function which is defined locally. Try writing your object in this way

const useTreeData = () => {
    return {
        saveTreeState() {
            // does something
        },
        onNodeChange() {
            // calls other functions
            this.saveTreeState();
        }
    };
};

Now if you try your test it should succeed. Or you can just add this in front of the saveTreeState()

const useTreeData = () => {

   function saveTreeState() {
     // does something
   }

   function onNodeChange() {
      // calls other functions
      this.saveTreeState();
   }

   return {
       saveTreeState,
       onNodeChange
   };
};

It is most probably going to error out in jslint, since this might be undefined depending on how you are going to instantiate the object.