StoryDecember 12, 20253 min read

šŸ—„ļø Mocking Storage & Caching in Jest — Expense Tracker Edition

Our Expense Tracker has learned a lot already.


šŸ—„ļø Mocking Storage & Caching in Jestā€Šā€”ā€ŠExpense TrackerĀ Edition

Image generated by Author usingĀ ChatGPT

Our Expense Tracker has learned a lot already.

It can bend time, survive failing APIs, spy on functions, even fire fake events whenever it feels like.

But today we face something far more dramatic.

Something every developer has suffered from:

STORAGE.

Yes… the place where data ā€œlives.ā€

Or sometimes dies without warning.

Or comes back as something you do not even remember saving.

This post is about mocking storage, caching, and everything your app ā€œremembersā€ā€¦

So your tests stop acting like they have memory loss.


šŸ’” Why Storage Is Annoying ToĀ Test

Image generated by Author usingĀ ChatGPT

Let’s be honestā€Šā€”ā€Šstorage is emotional.

When a function breaks, you fix it.

When an API fails, you retry it.

But when storage lies to you?

When you save something… and it comes back different?

That is BETRAYAL.

Every dev has felt this:

  • ā€œWhy is my saved list empty?ā€
  • ā€œWhy does this test pass only on Tuesdays?ā€
  • ā€œWho told localStorage to remember OLD data?ā€

Real apps read, write, update, and forget values constantly.

Tests must not depend on that chaos.

Tests need memory that behaves perfectly every time —

even if your users don’t. 😌

So today we fake it all.


🧩 Step 1ā€Šā€”ā€ŠOur StorageĀ Layer

Image generated by Author usingĀ ChatGPT

šŸ“ expenseStorage.js

exports.saveExpenses = (list) => {
  localStorage.setItem('expenses', JSON.stringify(list));
};

exports.loadExpenses = () => {
  const data = localStorage.getItem('expenses');
  return data ? JSON.parse(data) : [];
};

Looks harmless.

Until you run ten tests and all ten start sharing the same saved data.

That’s when you realize:

ā€œI am not testing storage… storage is testing me.ā€

āš™ļø Step 2ā€Šā€”ā€ŠMocking localStorage (The Only SafeĀ Way)

In Node, there is no real localStorage, so we create our own:

šŸ“ expenseStorage.test.js

beforeEach(() => {
  global.localStorage = {
    setItem: jest.fn(),
    getItem: jest.fn(),
  };
});

const { saveExpenses, loadExpenses } = require('./expenseStorage');

test('should save expenses', () => {
  const fake = [{ amount: 200 }];

  saveExpenses(fake);

  expect(localStorage.setItem).toHaveBeenCalledWith(
    'expenses',
    JSON.stringify(fake)
  );
});

test('should load expenses', () => {
  localStorage.getItem.mockReturnValue(JSON.stringify([{ amount: 300 }]));

  const result = loadExpenses();

  expect(result[0].amount).toBe(300);
});

Now storage behaves.

It acts exactly how you want…

not how the browser wants.

It is the first time storage has ever respected you. 😌

āš™ļø Step 3ā€Šā€”ā€ŠMocking AsyncStorage (ReactĀ Native)

jest.mock('@react-native-async-storage/async-storage', () => ({
  setItem: jest.fn(),
  getItem: jest.fn().mockResolvedValue(null),
  removeItem: jest.fn(),
}));

No device emulator.

No real storage.

No leftover data from your last test run.

React Native devs usually learn this trick the hard way.

YOU DID NOT.

🧠 Step 4ā€Šā€”ā€ŠMocking In-Memory Cache

Caching is basically your app saying:

ā€œI’ll remember this for later.ā€

And sometimes it does.

Sometimes it does not.

šŸ“ expenseCache.js

let cache = null;

exports.setCache = (data) => {
  cache = data;
};

exports.getCache = () => cache;

Test:

const { setCache, getCache } = require('./expenseCache');

test('should store and retrieve cache', () => {
  setCache({ amount: 150 });

  const result = getCache();

  expect(result.amount).toBe(150);
});

Simple. Predictable. No memory leaks.

No ā€œwhy is this test receiving data from last week???ā€

Real-World Psychology

Image generated by Author usingĀ ChatGPT

Your reader doesn’t stay because the code is correct.

They stay because the experience is familiar.

Everyone has dealt with:

  • stale cache
  • missing saved values
  • mysterious broken sessions
  • tests passing only after restart
  • data randomly reappearing

When you talk about things they’ve lived through…

they feel seen.

They keep reading.

This is how you create loyal readers, not just page views.

āœ… Summary

Image generated by Author usingĀ ChatGPT

Today, our Expense Tracker became smarter than memory itself.

It learned how to:

šŸ—„ļø Mock localStorage
šŸ“¦ Mock AsyncStorage
 🧠 Mock in-memory caching
✨ Avoid sticky data between tests

Your tests no longer depend on yesterday.

They run clean, isolated, and predictable —

the way tests should be.

Next post → we go into mocking failures, retries, and network disastersā€Šā€”ā€Šthe stuff every dev secretly fears but must master.

Thanks forĀ reading.

If this added value, follow me for more clear and practical posts.
— Alkesh Jethava