DEV Community

Bonnie Schulkin
Bonnie Schulkin

Posted on

Testing Functions within Functional React Components

People are switching to functional components in React, and class-based components are quickly becoming a thing of the past. However, with class-based components out of the picture, it’s much harder to access a function within your functional component for unit testing. A common question I get these days:

How do I test functions inside my functional component?

The short answer: you can’t. There is no way to access functions defined within a functional component for testing. But… if you’re reading this post, you probably don’t like that answer. Read on to see what your options are. 😊

Option 1: Don’t unit test the functions

Do any or all of the functions need to be unit tested? The trend in React testing these days is functional or behavioral testing, which means testing the interface between your app and your user. Unit testing functions means you’re testing internals (that is, testing your code, instead of testing your app). This is generally frowned upon because it makes your tests brittle and subject to rewriting upon refactor.

However, there are some cases where unit testing functions can be beneficial (for complex functions, or functions with edge cases that don’t affect the display layer). You can see this blog post for my recommendations on when to unit test within React apps. If these reasons fit your situation, take a look at the other options.

Option 2: Move Functions Outside the Component

Relocating functions to a custom hook makes them accessible for testing (plus you’re keeping business logic separate from the UI, in accordance with best practices). There’s even a library to help with testing custom hooks: react-hooks-testing-library.

Here’s an example of a custom hook with several related functions in my website code. Note that I don’t test this particular hook separately from the UI because it fits under this category in “When not to use the react-hooks library” : Your hook is easy to test by just testing the components using it.

Still, it’s an example of removing functionality from the functional component, so the component can focus solely on display. And I could access the functions within the hook for testing if they matched the reasons to use the react-hooks library:

  1. You’re writing a library with one or more custom hooks that are not directly tied to a component
  2. You have a complex hook that is difficult to test through component interactions

Option 3: Use Class-Based Components

This one might give you the heebie-jeebies — after all, you made a functional component to get away from the complexity of class-based components. However, you always have this option if the first two options don’t work for you. Class-based components served the React community well for many years, and they are still supported by React. And to the point of this blog post, you can easily access functions within a class-based component as a property of the component.


So there you have it: three options for testing functions within a functional component (none of which actually test functions within a functional component, since that’s just not possible).

Top comments (3)

Collapse
 
stefaneidelloth profile image
stefaneidelloth • Edited

Thank you for this very interesting article. Below is a variant of your option 2 that tries to combine the benefits of both worlds. (I don't have much experience with React.)

a) Let the component itself be a function and
b) Use classes to structure the called code and make it accessible for unit testing

The imported class DescriptionLoader defines a custom hook. That class might include sub functions. The helper class _Foo is exported as a whole and exposes its static sub functions. If wanted, all sub functions can be tested as individual units.

Example:

import DescriptionLoader from './description-loader';

export default function Foo(properties){
  const [descriptions, setDescriptions] = React.useState([]);
  DescriptionLoader.load(properties, setDescriptions);  //custom Hook defined in imported class
  const extendedProperties = { ...properties, descriptions };

  return extendedProperties.initialized
    ? _Foo.initializedRender(extendedProperties)
    : _Foo.defaultRender(extendedProperties);
}

//Helper class that is defined in the same file. Its exported and therefore accessible for unit testing.
export class _Foo { 
  static initializedRender(properties){
    if (descriptions && descriptions.length > 0){
    //... use loaded descriptions here...
   }
 }

  static defaultRender(properties){
    return <div>Loading...</div>
 }
}

}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tagizadeorxan profile image
tagizadeorxan

hi, still same yeah cant test functions inside functional component?

Collapse
 
bonnie profile image
Bonnie Schulkin

Yep, this hasn't changed.