From adopting spaghetti code to 100% test coverage
Tags: enzyme, testing, reactnative, javascript
Published: 2022-07-08 16:05:00

`// JEST and Enzyme for React Native App`

Also see part 2 here: [Why Shallow Tests?](https://dev.to/c6z3h/part-2-shallow-tests-d57)

Well, hopefully you reading this are not given a short few days to write tests for something you never seen before. That was my experience, but it happened because I was trying to help my team hit the crazy deadline that was just over
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywkac3nbuzuxmv50ngz8.png)

## Disclaimer

- Unless your APP / program is REALLY huge (like Facebook), manual testing may be more time-savvy than writing unit tests
- This article summarizes all my discoveries over 3 work days writing tests. Hopefully that gets your tests up faster

## Benefits of testing
1. Catch bugs you did not see before (eg. 2 exported functions with same name but from different files, and slightly different implementation).
2. Understand code that you haven't seen before.

## Table of Contents
1. File Naming
2. Test DOM props OR function returns? 
3. Mocking Functions
4. Mocking Hooks
5. async / awaits
6. Debug failing tests
7. Others

## 1. File Naming
For ease of reference, just add `.test.` to your actual file(s) name. Mimicking the folder structure will help organize things too. In this case, we have a component `ITerms` within the page `IdemnityForSpaceTravel`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jdzce3p0u03h9fp2b3r.png) ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a692l8xpgo8p8ix6irib.png)

## 2. Test DOM props OR function returns
This article will focus on mount(<Component/>) method from Enzyme. If you are just mocking function return values, don't waste time here! Jest documentation should be plenty to get started.

```
describe('@pages/IdemnityForSpaceTravel/ITerms', () => {
  it('getIdemnityIterms() is called correctly', () => {
    // const wrapper =
    mount(
      <Wrap>
        <ITerms />
      </Wrap>
    );
    expect(getIdemnityIterms).toBeCalled();
  });
...
});
```

## 3. Mocking (nested) Functions
- mocking default export function
```
jest.mock('./esModule', () => ({
  __esModule: true, // this property makes it work
  default: 'mockedDefaultExport',
}));
```
- mocking named export function(s)
```
jest.mock('@pages/IdemnityForSpaceTravel/ITerms/config', () => ({
  getIdemnityIterms: jest.fn(),
  getUserType: jest.fn(),
});
```
- partial mocking named export function(s)
Uses some actual functions from the file.
```
jest.mock('@pages/IdemnityForSpaceTravel/ITerms/config', () => ({
  ...jest.requireActual('@pages/IdemnityForSpaceTravel/ITerms/config'),
  getIdemnityIterms: jest.fn(),
});
```
- mocking a nested function (with Promise / without Promise)
This is for the case when you want to mock a function within the function on your page / component. Use the same methods as above, but find the right file to import from.

- mocking actions
Button presses:
`wrapper.find(TouchableWithoutFeedback).prop('onPress')?.();` or `wrapper.find(TouchableWithoutFeedback).simulate('click')`

## 4. Mocking Hooks
Sometimes, Jest isnt that helpful in telling you why it fails...
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jc5sxbf25cvav91176j8.png)

- Custom Hooks

Let's say you had a custom hook `useError.tsx` that has the structure of
```
export default () => {
  ...
  return { ErrorComponent, getErrorComponent };
};
```
used in main file like ` const { ErrorComponent } = useError();`, do this:
```
jest.mock('@utils/index', () => {
  return {
    __esModule: true,
    useError: () => ({ ErrorComponent: 'mocked_value' })
  };
});
```

- useState() or useEffect() hooks
In the case where you have `useEffect(() => { ... }, [])` that runs on initial render, updating the state of the page / component, use `wrapper.update()` to update the DOM props.

- redux useSelector() hooks
In this case, this is what you need in the `.test.` file.
```
import * as redux from 'react-redux';
...
const mockUsername = 'zelenskyyy';
...
describe('@pages/IdemnityForSpaceTravel', () => {
  beforeEach(() => {
    const spy = jest.spyOn(redux, 'useSelector');
    spy.mockReturnValue({
      username: mockUsername
    });
  });
  ...
});
```

## 5. async / awaits
Flushing Promises are a good way to ensure the async calls are completed before you assert tests. In below example, we see how we mock test an async function getIdemnityIterms in (1) Promise Resolved, (2) Promise Rejected.
```
// definition for flushing promises
const flushPromises = () => new Promise(setImmediate)

// mock the function as a generic jest.fn
jest.mock('@pages/IdemnityForSpaceTravel/ITerms/config', () => {
  return {
    __esModule: true,
    getIdemnityIterms: jest.fn(),
  };
});

// tests
describe('@pages/IdemnityForSpaceTravel', () => {
  it('promise works out', async () => {
    // mock jest.fn implementation: Promise is resolved
    (getIdemnityIterms as any).mockImplementation(() =>
      Promise.resolve({ ITerms: 'this is my iterm' });

    const wrapper = mount(
      <Wrap>
        <ITerms>
      </Wrap>
    );

    await flushPromises();
    wrapper.update();

    expect(getIdemnityIterms).toBeCalled();
    expect(wrapper.find(Text).prop('value')).toBe('this is my iterm');
    // Finds the 'value' of Text component in ITerms
  });

  it('feeling betrayed', async () => {
    // mock jest.fn implementation: Promise is rejected
    (getIdemnityIterms as any).mockImplementation(() =>
      Promise.reject());

    const wrapper = mount(
      <Wrap>
        <ITerms>
      </Wrap>
    );

    await flushPromises();
    wrapper.update();

    expect(getIdemnityIterms).toBeCalled();
    expect(wrapper.find(Text).prop('value')).toBe('');
    // Finds the 'value' of Text component in ITerms
  });
...
});
```

## 6. Debug failing tests
**1. Use Jest built-in Istanbul to see which lines need coverage.**
Run Jest with `--coverage` flag, open the `coverage/Icov-report/index.html` in Finder / File Explorer and double-click it. This should give you all the files and how much coverage it has.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8goj5m7yud5y6j7jxrg.png) Open each file to see which lines are not covered.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5grilqfxrt157dn7j1ra.png) Here, RED lines 44 and 45 means it is not covered.
 
**2. Comment out everything**
Comment out everything. Then uncomment each <u>component</u> in the render tree and run test. This helps you figure out where the test is failing. You can even do this at the <u>prop</u> level

**3. Log the DOM Tree**
`console.log(wrapper.debug())` to see the whole render tree.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9nw9htcn662i1yjfiad.png) `// Would you just look at the <View> ?`
If you have a very deep-nested DOM tree like above, you can zoom into each part like
```
console.log(wrapper.find(Touchable).debug()) // find all Touchable component(s)
console.log(wrapper.find(Touchable).at(1).debug()) // find Touchable component at position 1 (0-indexed)
console.log(wrapper.find(Touchable).find(TextInput).debug()) // find the TextInput component inside the Touchable component
```
**4. Take a break!**
Sometimes you just forgot to save changes, or typo-ed somewhere.

## 7. Others
**1. Jest configurations**
Having looked at so much for tests in such short time, I just feel like
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yd042x9jy6ueu7r87cf8.gif) Go figure this yourself!

**2. Running Individual tests**
This is mainly to reduce time spent on debugging tests.

Option 1: Running test with flag
With this, you can get to run individual test files. For example, if you have a test file Spaghetti.test.tsx, run `npm run test -- Spaghetti.test.tsx`

Option 2: Adding a VSCode Plugin
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zwmhtt2tc0d0i2itk74.png) Check this [post](https://dev.to/nithinpeter/jest-run-it-run-and-debug-individual-jest-tests-from-vs-code-e05) on how to start it.
**Pro**: Quicker test debugging. Runs single tests instead of ALL existing tests.
**Con**: Initial setup time required.

**3. Test Coverage**
Run JEST `--coverage` flag to see how much tests are covering. Things like `if / else` branch, function call coverage.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k05x3eb961ef5expaeii.jpg)
 
**4. [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)**
Some forums say it is better, maybe it is a viable alternative.