Hey everyone, recently I struggled a bit trying to produce some tests for local storage using the testing library. The examples I found online were scarce, or just a bit confusing for the typical reader or someone with little or no knowledge.

After digging around, I came up with something I may consider to be an acceptable solution. Before delving into code, let’s review the basics.

What is the local storage?

The local storage is a property that allows accessing a Storage object that allows saving key/value pairs that persist between sessions and has no expiration date.

A storage object exposes a get, set, and remove Item methods that allow reading, adding, and removing items from the local storage.

How to test?

The main thing here passes by mocking the above-referred methods.

We can do that by using the Object.defineProperty() to modify the local storage methods and pass them mock functions.

Mocking local storage

As you can see, we replace the implementation of the getItem and setItem with a jest.fn(). Now we can access the window.localStorage.getItem freely and do assertions over it.

You may be wondering what that writable is there for, right?

The writable attribute allows reassigning values to that property, at will, by using an assignment operator. That means that if we decide that we want to access the localStorage.setItem outside that defineProperty method and modify its content we can.

With that information on your belt, let’s queue in a React component and test it using the testing library.

The component

Local storage component

Above, you can see a functional component that leverages the useLocalStorage custom hook. (Check https://usehooks.com/useLocalStorage/ for more info and custom hooks).

The component has an input that receives a name stored on the local storage and an onChange event that updates the local storage. For more fun and an extra difficulty, we add a fetch function that receives an endpoint, and, using axios, we make a get request and save the response data to our local storage.

The tests

We will be covering the following test cases:

On render, the local storage getItem should be called. On the input text change, we should call the local storage setItem with the new text. On button click, we should make a get request to the endpoint and store the response on local storage.

First thing, first let’s mock our dependencies and setup or tests

Mocking dependencies and setting tests up

As you can see on the gist above, we mock the axios get method to return a promise and leverage jest beforeEach function to redefine our local storage mocks before every test case.

Finished the setup, the tests should be straightforward.

On render, the local storage getItem should be called.

Assert getItem call

This test case is the most straightforward of the three. All we have to do is render our component and assert that our mocked getItem is called.

On the input text change, we should call the local storage setItem with the new text

Assert setItem is called on input change

This test case needs interaction with our component to be able to validate if the setItem is called. We do this by rendering our component and extracting its queryByPlaceholderText method and make use of it to gain access to our input. Having our input component, we now can use the testing library fireEvent to change its value.

The rest is simple, we assert that our setItem is called one time, and afterward, we check if that call was done using the expected key/value pair.

On button click, we should make a get request to the endpoint and store the response on local storage.

This test is trickier since it deals with asynchronous content, luckily we already mocked the axios get method always to return a given string.

Assert network request is made and setItem is called

The first thing we need to do is to start our async request. This part can be done by getting our button a firing a click event on it. Now we don’t want our assertions to be done before we make sure that the async requests have ended. Since we know that after finishing the request, our input value will change, then we can use the waitForElement and the getByDisplayValue to wait for the mocked value to appear.

Afterward, we can do our assertions successfully, and everything works right.

Now you can check our complete test file

Complete local storage test

You can also play around with it on Codesandbox

Play around on Codesandbox

Conclusion

As you can see testing, local storage can be effortless, or it can be an earful. It all depends on how you approach it.

I hope this article helped with eventual doubts you may have about local storage testing as well as using the testing library for this end.

If you have any questions, you can find me on Twitter at @danieljcafonso

I hope you enjoyed and stay tuned for the next guides.