StoryDecember 23, 20253 min read

Mocking Permissions, Sensors & Device Behaviors in Jest

šŸ›Žļø When Your App Wants to Notify the User…


Mocking Permissions, Sensors & Device Behaviors inĀ Jest

Image generated by Author usingĀ ChatGPT

šŸ›Žļø When Your App Wants to Notify the User…

Our Expense Tracker is now a grown-up app.

It calculates totals…

fetches APIs…

survives network disasters…

and even remembers things better than we do.

So now it wants to do something meaningful:

šŸ‘‰ Remind the user to add expenses.

But there’s a problem.

A UNIVERSAL problem.

The user.

May.

Say.

ā€œNO.ā€

Yes.

Notification permissions are that polite request your app makes…

and the user rejects like a spam call.

So today we learn to mock notification permissions, so your tests do not break when real users decide to betray you.

šŸ’” Why Notification Permissions NeedĀ Mocking

Image generated by Author usingĀ ChatGPT

In real apps, notifications break for reasons nobody understands:

  • The user denied permission
  • iOS silently blocks your request
  • Android ā€œoptimizes batteryā€ (translation: kills everything)
  • Notification service returns empty responses
  • Some devices delay notifications by 6 hours
  • Some fire twice
  • Some never fire at all

Testing this in real life is impossible.

Testing in Jest?

Easy.

Fast.

Predictable.

And you do not need 12 devices on your desk.

🧪 Step 1ā€Šā€”ā€ŠOur Notification Permission Function

Let’s say our Expense Tracker checks permission before sending reminders:

šŸ“ notificationPermission.js

exports.checkNotificationPermission = async () => {
  return Notification.requestPermission(); 
};

In browsers this returns "granted", "denied", or "default".

On devices, libraries like Expo/React Native behave similarly.

We want to test how our logic reacts to each state.

🧪 Step 2ā€Šā€”ā€ŠMock the Permission API

šŸ“ notificationPermission.test.js

global.Notification = {
  requestPermission: jest.fn(),
};

const { checkNotificationPermission } = require('./notificationPermission');

test('should handle granted permission', async () => {
  Notification.requestPermission.mockResolvedValue('granted');

  const result = await checkNotificationPermission();

  expect(result).toBe('granted');
});

This is clean.

Simple.

Predictable.

And you never have to rely on real OS permission dialogs.

You have definitely seen this:

You test notifications on Android.
Ā They work.
Ā You test on iOS.
Ā They don’t.
Ā You test again.
Ā They work.
Ā You hand the device to QA.
Ā They stop working.
You reinstall the app.
Ā They work again.
Ā You push to production.
Ā They never work again.

Mocking avoids this emotional rollercoaster.

🧪 Step 3ā€Šā€”ā€ŠMock ā€œDeniedā€ Permission

test('should handle denied permission', async () => {
  Notification.requestPermission.mockResolvedValue('denied');

  const result = await checkNotificationPermission();

  expect(result).toBe('denied');
});

Your app should gracefully handle a user who says ā€œno thanks.ā€

🧪 Step 4ā€Šā€”ā€ŠMock ā€œDefaultā€ (User IgnoredĀ Prompt)

On web and some RN libs, "default" means user dismissed the popup without answering.

test('should handle default permission', async () => {
  Notification.requestPermission.mockResolvedValue('default');

  const result = await checkNotificationPermission();

  expect(result).toBe('default');
});

This case is often forgottenā€Šā€”ā€Šbut very real.

šŸ”„ Step 5ā€Šā€”ā€ŠMocking Actual Notification Delivery

Imagine this function:

šŸ“ sendNotification.js

exports.sendReminder = async () => {
  if (Notification.permission !== 'granted') return false;
  return Notification.show('Time to log expenses!');
};

We mock this too:

global.Notification = {
  permission: 'granted',
  show: jest.fn(),
};

const { sendReminder } = require('./sendNotification');

test('should send notification when permission granted', async () => {
  const result = await sendReminder();

  expect(Notification.show).toHaveBeenCalled();
  expect(result).toBeTruthy();
});

Now you test the entire flow:

permission → delivery → success.

Psychology

Image generated by Author usingĀ ChatGPT

Developers relate deeply to notification bugs because:

  • They think it’s working (it’s not)
  • They think it’s not working (it suddenly is)
  • iOS Simulator lies
  • Android background restrictions change every OS update
  • Expo’s notification service behaves differently across devices

When your writing points to these experiences, readers feel:

ā€œYESā€Šā€”ā€ŠI’ve suffered this too.ā€

And that’s why they keep reading your series.

This is how you build trust.

āœ… SUMMARY

Image generated by Author usingĀ ChatGPT

Today our Expense Tracker learned how to talk to the user politely…

and how to survive emotional rejection.

We learned:
Ā šŸ›Žļø Mocking notification permissions (granted / denied / default)
Ā šŸ“© Mocking actual notification delivery
Ā āŒ Avoiding OS-level unpredictability
Ā šŸ” Making tests consistent across devices

Your tests now understand human behavior:

Most people say ā€œNot Now.ā€

Your app says ā€œNo problem.ā€

Next post → Mocking Sensors & Device Behaviors (GPS, Motion, Orientation)
We’re going deeper.

Thanks forĀ reading.

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