DEV Community

Kinga
Kinga

Posted on • Edited on

1

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

Image of Datadog

The Future of AI, LLMs, and Observability on Google Cloud

Datadog sat down with Google’s Director of AI to discuss the current and future states of AI, ML, and LLMs on Google Cloud. Discover 7 key insights for technical leaders, covering everything from upskilling teams to observability best practices

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more