Kinga
Posted on July 20, 2023
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);
});
}, []);
BreadcrumbsHelper.ts
export default class BreadcrumbsHelper {
constructor(protected dataProvider: BreadcrumbDataProvider, protected pageContextProvider: PageContextDataProvider) {}
public getBreadcrumbs = async (): Promise<IBreadcrumbItem[]> => {
const items: IBreadcrumbItem[] = [];
//...
return items;
}
//...
}
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([];
})
};
}),
};
});
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)
})
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} />);
});
//...
});
}
Resources
Posted on July 20, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.