# Testing Your Effects

## The Problem

The problem you will initially run into is this: I just want to test an effect, but I don't know how to run the effect on its own. Well, maybe you don't need to run the effect in isolation, and run it through a test app. This will keep your test reflecting the reality of how it is used, while also helping you isolate how the effect affects the state.

## The Solution: End to End

Let's take the Custom Effects section example of notificationPermission and notification effects.

{% code title="notificationEffects.js" %}

```javascript
import { effects } from 'ferp'

export const notificationPermissionEffect = (
  grantEffect,
  denyEffect = effects.none,
  dismissEffect = effects.none,
  errorEffect = effects.none
) => effects.defer(
  Notification.requestPermission()
    .then((result) => {
      switch (result) {
        case 'granted':
          return grantEffect();
        case 'denied':
          return denyEffect();
        default:
          return dismissEffect();
      }
    })
    .catch(errorEffect)
);

export const notificationEffect = (title, options = {}, resultEffect = effects.none) => effects.thunk(() =>
  resultEffect(new Notification(title, options))
);
```

{% endcode %}

In this case, we have some browser specific code (ie the Notification class), so we'll just make a global/controllable mock for it. If you are building a browser-based ferp app, you may want to look into a modern browser based test framework. Let's set up a test using describe/it blocks, similar to what you'd seen in mocha or jest:

{% code title="notificationEffects.test.js" %}

```javascript
import { app, effects } from 'ferp';
import { notificationPermissionEffect, notificationEffect } from './notificationEffects.js';

describe('Notification effects', () => {
  describe('notificationPermissionEffect', () => {
    const injectNotificationClass = (permissionResponse) => {
      global.Notification = class {
        static requestPermission() {
          return permissionResponse;
        }
      }
    };
    
    afterEach(() => {
      delete global.Notification;
    });
    
    const createTestApp = (onComplete) => {
      const setterEffect = (value) => () => value;
      const detach = app({
        init: [
          null,
          notificationPermissionEffect(
            setterEffect('grantEffect'),
            setterEffect('denyEffect'),
            setterEffect('dismissEffect'),
            setterEffect('errorEffect'),
          ),
        ],
        update: (message, state) => {
          onComplete(message);
          detach();
          return [state, effects.none()];
        },
      });
      
    };
    
    it('calls the grantEffect', (done) => {
      injectNotificationClass(Promise.resolve('grant'));
      createTestApp((whichEffect) => {
        expect(whichEffect).toBe('grantEffect');
        done();
      });
    });

    it('calls the denyEffect', (done) => {
      injectNotificationClass(Promise.resolve('deny'));
      createTestApp((whichEffect) => {
        expect(whichEffect).toBe('denyEffect');
        done();
      });
    });

    it('calls the dismissEffect', (done) => {
      injectNotificationClass(Promise.resolve('dismiss'));
      createTestApp((whichEffect) => {
        expect(whichEffect).toBe('dismissEffect');
        done();
      });
    });

    it('calls the errorEffect', (done) => {
      injectNotificationClass(Promise.reject());
      createTestApp((whichEffect) => {
        expect(whichEffect).toBe('errorEffect');
        done();
      });
    });
  });

  describe('notificationEffect', () => {
    const injectNotificationClass = () => {
      global.Notification = class {
        constructor(title, options) {
          this.constructorArgs = [title, options];
        }
      }
    };
    
    afterEach(() => {
      delete global.Notification;
    });
    
    const createTestApp = (title, options, onComplete) => {
      app({
        init: [
          null,
          notificationEffect(title, options, onComplete),
        ],
      });
      
    };
    
    it('creates a notification', (done) => {
      injectNotificationClass();
      createTestApp('Hello world', { body: 'From Ferp.JS' }, (notification) => {
        expect(notification).toBeInstanceOf(Notification);
        expect(notification.constructorArgs).toEqual(['Hellow world', { body: 'From Ferp.JS' }]);
        done();
        return effects.none();
      });
    });
  });
});
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ferp.mrbarry.com/understanding-effects-in-ferp/testing-your-effects.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
