Charklewis
Charklewis

Reputation: 5621

How to test functions in a function using Jest

I have some code that has functions inside functions, and I want to be able to unit test the functions inside the parent function.

I am looking to have tests that unit test these and spy on them (both requirements are needed).

Example:

export default parentFunction = () => {

  const innerFunction = () => {
    //that does stuff
  }

  const anotherInnerFunction = () => {
    //that does more stuff
  }

  //and at some point, the functions are called

  //like this
  innerFunction()

  const anotherFunction = () => {
    //or like this
    anotherInnerFunction()  
  }
}

I have not been able to find a way to test these inner functions. I have tried the following.

Example test

import parentFunction from "myfile"

it("should call innerFunction", () => {
  //this causes an error in jest
  const innerFunctionSpy = jest.spyOn(parentFunction, "innerFunction")
  //..etc
  expect(innerFunctionSpy).toHaveBeenCalled()
})

it("will return a value from anotherInnerFunction", () => {
  //this does not work
  const value = parentFunction.anotherInnerFunction()
  //this also does not work
  const value = parentFunction().anotherInnerFunction()
  //..etc
})

Does the parent function need to be refactored in order to be able to tests these inner functions? If my parent function was an object then I could test these, however, I am not sure if I can refactor my code to work like this.

For example

export default parentFunction = {
  innerFunction: () => {
    //that does stuff
  },
  //more code
}

Upvotes: 0

Views: 286

Answers (1)

Fullstack Guy
Fullstack Guy

Reputation: 16908

You cannot access the variables or functions scoped inside another function in JavaScript. Unless you explicitly expose them by returning them from that function or export them from the module. This is not about Jest, this is how it works in JavaScript.

jest.spyOn(parentFunction, "innerFunction")

The above line of code indicates to Jest that the innerFunction function is set as a property of the parentFunction object but that is not the case. In fact innerFunction is a function scoped inside the parentFunction which cannot be accessed from outside of the scope of parentFunction. Unless you return it explicitly or define it on the module level scope and then export it.

But the inner workings or the implementation details of such inner functions should not be exposed, but if it is needed it should be marked as such using an _ before its name, take the following example:

 //scoped to the module
 const _innerFunction = () => {
    //that does stuff
 }

 //scoped to the module
 const _anotherInnerFunction = () => {
    //that does more stuff
 }

 //exported as a public API
 const anotherFunction = () => {
     _anotherInnerFunction()  
 }


const publicApi = {
  anotherFunction,
  // expose the private functions for unit tests
  _innerFunction,
  _anotherInnerFunction 
}

export default publicApi;

Then in your Jest test case:

import publicApi from "myfile"

it("should call anotherFunction", () => {
  const anotherFunctionSpy = jest.spyOn(publicApi, "anotherFunction")
  //..etc
  expect(anotherFunctionSpy ).toHaveBeenCalled()
})

it("should call _innerFunction", () => {
  const innerFunctionSpy = jest.spyOn(publicApi, "_innerFunction")
  //..etc
  expect(innerFunctionSpy ).toHaveBeenCalled()
})

Upvotes: 1

Related Questions