question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

What’s the best method for testing functions around Recoil? For example, if I had a function like this:

export const setAddComplete =
    (addComplete: boolean, setNotesState: SetterOrUpdater<State>) => {
        setNotesState(state => {
            return {
                ...state,
                addComplete,
            };
        });
    };

How should I test it? I noticed the TestingUtils folder, but it looks like they aren’t included in the package.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
acutmorecommented, May 20, 2020

Hi @adrianbw. Recoil requires React to run. You can test your Recoil Atoms and Selectors by creating a small React component that uses them and testing that in the way you would test any React component. https://reactjs.org/docs/testing.html

For example:

const React = require("react");
const TestRenderer = require("react-test-renderer");
const Recoil = require("recoil");
const mySelector = require("../path/to/your/selector");

test("...", () => {
  let value = null;

  function TestSelector() {
    const selectorValue = Recoil.useRecoilValue(mySelector);
    React.useEffect(() => {
      value = selectorValue;
    });
    return null;
  }

  TestRenderer.act(() => {
    TestRenderer.create(<Link page="https://www.facebook.com/">Facebook</Link>);
  });

  expect(value).toEqual(expectedValue);
});

If you want to just test your state update function and not run Recoil then you could extract, export and test that code in isolation.

Before:

export const setAddComplete = (
  addComplete: boolean,
  setNotesState: SetterOrUpdater<State>
) => {
  setNotesState((state) => {
    return {
      ...state,
      addComplete,
    };
  });
};

After:

// Can directly test this function without needed Recoil
export updateState(state, addComplete) {
  return {...state, addComplete };
}

export const setAddComplete = (
  addComplete: boolean,
  setNotesState: SetterOrUpdater<State>
) => {
  setNotesState(updateState);
};
1reaction
adrianbwcommented, May 21, 2020

For anyone who comes upon this and likes to write unit tests (a discussion I’m not going to get into), here’s a pattern I adopted, which uses jest.fn() to report out the atom’s value.

interface IGenericUpdater<ValType, StateType extends object> {
    (value: ValType, setState: SetterOrUpdater<StateType>): void;
}

interface ISetterTest<U, V extends object> {
    atom: Atom;
    function: IGenericUpdater<U, V>;
    value: U;
}

type UpdaterFunction = (property: any, updaterFunction: SetterOrUpdater<any>) => void;

type TestComponentProps = {
    atom: Atom;
    function: UpdaterFunction;
    value: any;
    reporterFunction: (state: State) => void;
};

const TestComponent: React.FunctionComponent<TestComponentProps> = (props: TestComponentProps) => {
    const [state, setState]: [State, any] = useRecoilState(props.atom);
    React.useEffect(() => {
        props.function(props.value, setState);
        props.reporterFunction(state);
    },              [state]);
    return <div />;
};

const createMountedWrapper = (additionalProps: Partial<TestComponentProps>) => {
    const props = {
        atom: null as any,
        function: jest.fn() as any,
        value: null as any,
        reporterFunction: jest.fn() as any,
        ...additionalProps,
    };
    const wrapper = mount(<RecoilRoot><TestComponent {...props} /></RecoilRoot>).rendered;
    const instance = wrapper.find(TestComponent).instance() as any;
    return {wrapper, instance, ...props};
};

describe('recoil tests', () => {
    let componentWrapper: any;
    afterEach(() => {
        if (componentWrapper?.unmount) {
            componentWrapper.unmount();
        }
    });
   it('setAddComplete', () => {
        const props: ISetterTest<boolean, State> = {
            atom: notesStore,
            function: setAddComplete,
            value: true,
        };
        const { wrapper, reporterFunction } = createMountedWrapper(props);
        componentWrapper = wrapper;
        expect(reporterFunction).toHaveBeenCalledWith({...initState, addComplete: props.value});
    });
});

Before adding wrapper.unmount(), I got in some loops with useEffect testing multiple functions (presumably because it was adding an instance on every mount?). There may be other ways to prevent this that I don’t know of.

Read more comments on GitHub >

github_iconTop Results From Across the Web

COVID-19 Testing: What You Need to Know - CDC
Viral tests look for a current infection with SARS-CoV-2, the virus that causes COVID-19, by testing specimens from your nose or mouth. There...
Read more >
Community-Based Testing Sites for COVID-19 - HHS.gov
Find Testing Resources in Your State. COVID-19 tests are available to everyone in the U.S., including the uninsured. Select your state below to...
Read more >
Testing.com: Order Lab Tests and Blood Tests Online
Hundreds of easy-to-read lab testing guides. Confidential, secure and convenient online lab test ordering powered by trusted physician networks. Compassionate ...
Read more >
Get free at-⁠home COVID-⁠19 tests this winter
15,000+ Free Testing Sites. No-cost antigen and PCR COVID-⁠19 tests are available to everyone in the U.S., including the uninsured, at more than...
Read more >
Find COVID-19 Tests
Find COVID-19 tests near you. Four ways to get tested: free community events or fixed test sites in NC, your medical provider or...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found