DEV Community

Kinga
Kinga

Posted on • Edited on

Jest + React component

Test driven development

If you are new to TDD, or jest, please have a look at the Resources section at the end of this post.

The code

I have a React component, SiteBreadcrumbs.tsx that is using BreadcrumbsHelper class to generate site breadcrumbs.

SiteBreadcrumbs.tsx

import BreadcrumbsHelper from '../../../../utils/BreadcrumbsHelper';
import { BreadcrumbDataProvider } from '../../../../dal/BreadcrumbDataProvider';
import { PageContextDataProvider } from '../../../../dal/PageContextDataProvider';

React.useEffect(() => {
    //...
    const helper = new BreadcrumbsHelper(dataProvider, pageContextProvider);

    helper.getBreadcrumbs()
        .then((result) => {
             setLinkItems(result);
         })
         .catch((error) => {
             console.log(error);
          });
}, []);
Enter fullscreen mode Exit fullscreen mode

BreadcrumbsHelper.ts

export default class BreadcrumbsHelper {

constructor(protected dataProvider: BreadcrumbDataProvider, protected pageContextProvider: PageContextDataProvider) {}

    public getBreadcrumbs = async (): Promise<IBreadcrumbItem[]> => {
        const items: IBreadcrumbItem[] = [];
        //...
        return items;
    }
    //...
}
Enter fullscreen mode Exit fullscreen mode

Tests

Not so great approach

Jest allows mocking partials, so I could mock the BreadcrumbsHelper.getBreadcrumbs() method using the following code:

SiteBreadcrumbs.test.tsx
jest.mock('../../src/utils/BreadcrumbsHelper', () => {
    const originalModule = jest.requireActual('../../src/utils/BreadcrumbsHelper');
    return {
        __esModule: true, //<- required for ES modules
        ...originalModule,
        default: jest.fn().mockImplementation(() => {
            return {
                getBreadcrumbs: jest.fn().mockImplementation(() => {
                    return Promise.resolve([];
                })
            };
        }),
    };
});

Enter fullscreen mode Exit fullscreen mode

But in my tests, I want to cover different cases: breadcrumbs for site's welcome page, site's (other) page, subsite, etc. And I certainly won't be creating separate files just to cover them.

The right approach

You will find many examples on how to mock functions, but the complicated part here is that getBreadcrumbs is a class method and is not exported as a function.

Jest requires the jest.mock('../../src/utils/BreadcrumbsHelper') to be above the tests, but I can change the getBreadcrumbs mock for each test using.

(BreadcrumbsHelper as jest.Mock).mockReturnValueOnce({
    getBreadcrumbs: jest.fn().mockResolvedValue(BreadcrumbsConfig.TeamSiteWelcomePage)
})
Enter fullscreen mode Exit fullscreen mode
SiteBreadcrumbs.test.tsx
import BreadcrumbsHelper from '../../src/utils/BreadcrumbsHelper';
jest.mock('../../src/utils/BreadcrumbsHelper')

const BreadcrumbsConfig = {
    TeamSiteWelcomePage: [
        { "text": "Intranet", "key": "1", "href": "https://contoso.sharepoint.com/sites/Intranet"}
    ],
    TeamSiteInsightsPage: [
        { "text": "Intranet", "key": "1", "href": "https://contoso.sharepoint.com/sites/Intranet"},
        { "text": "Insights", "key": "2", "href": ""}
    ],
    //...
}

describe('SiteBreadcrumbs', () => {
    it('should render breadcrumbs on TeamSite WelcomePage', async () => {
        (BreadcrumbsHelper as jest.Mock).mockReturnValueOnce({
            getBreadcrumbs: jest.fn().mockResolvedValue(BreadcrumbsConfig.TeamSiteWelcomePage)
        })
        await act(async () => {
            siteBreadcrumbs = render(<SiteBreadcrumbs pageContext={mockPageContext} spfiContext={mockSPFI} />);
        });
        //...
    });

    it('should render breadcrumbs on TeamSite InsightsPage', async () => {
        (BreadcrumbsHelper as jest.Mock).mockReturnValueOnce({
            getBreadcrumbs: jest.fn().mockResolvedValue(BreadcrumbsConfig.TeamSiteInsightsPage)
        })

        await act(async () => {
            siteBreadcrumbs = render(<SiteBreadcrumbs pageContext={mockPageContext} spfiContext={mockSPFI} />);
        });
        //...
    });
}
Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (0)