šļø Mocking Storage & Caching in Jest ā Expense Tracker Edition
Our Expense Tracker has learned a lot already.
šļø Mocking Storage & Caching in JestāāāExpense TrackerĀ Edition

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

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

š 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

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

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