Skip to content

Instantly share code, notes, and snippets.

@cmonacaps
Created August 9, 2019 15:02
Show Gist options
  • Save cmonacaps/f45871a1e50f306fd497cb1cb6577c59 to your computer and use it in GitHub Desktop.
Save cmonacaps/f45871a1e50f306fd497cb1cb6577c59 to your computer and use it in GitHub Desktop.
jest.fn() is weird
// Consider that some application logic defines:
//
// const serviceRequests = {
// getShoppingCart: customerId => makeHttpGetRequest(`/shopping_cart/${customerId}`),
// getOrderHistory: customerId => makeHttpGetRequest(`/order_history/${customerId}`),
// };
//
// const CustomerMenu = ({customerId, requests}) => (<div>...</div>);
//
// and that always supplies 'serviceRequests' as the CustomerMenu 'requests' prop.
describe('CustomerMenu', () => {
it('uses the supplied `requests` object as expected', () => {
const mountCustomerMenuAndSimulateSomeUserActions = (customerId, requests) => {
// This function implementation is wholly contrived
//
// In the real world it would mount the CustomerMenu component and simulate some UI events,
// which might, in some case, be expected to result in this use of the 'requests' object:
requests.getOrderHistory(customerId);
};
const mockCustomerId = 1234;
const mockRequests = {
getShoppingCart: jest.fn(),
getOrderHistory: jest.fn(),
};
mountCustomerMenuAndSimulateSomeUserActions(mockCustomerId, mockRequests);
expect(mockRequests.getShoppingCart.mock.calls).toEqual([]);
expect(mockRequests.getOrderHistory.mock.calls).toEqual([[mockCustomerId]]);
// Now, consider that during development, many more entries may be added to the 'serviceRequests' object.
// Furthermore, many 'CustomerMenu' user interactions would result in only one call to one serviceRequest
// entry.
//
// For each test case then, the expectation would be:
// 1. a call is expected to one particular entry, with some particular parameters
// 2. all other entries will have no calls
//
// When adding a new entry, we wouldn't want to update all the existing test cases to manually check that
// there are no calls to the new entry. And we don't want the unite test full of copy-and-pasted
// boilerplate.
//
// So we'll try to factor this into a function that all the test cases can call.
const verifyRequestCalls = expectedCalls => {
// Iterate through all the defined entries. If a key is found in 'expectedCalls', verify the parameters
// match. Otherwise, verify there were no calls.
const mockRequestEntries = Object.entries(mockRequests);
mockRequestEntries.forEach(([entryName, mock]) => {
if (expectedCalls[entryName]) {
expect(mock.calls).toEqual(expectedCalls[entryName]);
} else {
expect(mock.calls).toEqual([]);
}
});
};
// That looks about right, let's try it
verifyRequestCalls({ getOrderHistory: [[mockCustomerId]] });
// Unfortunately, though, it says:
//
// Comparing two different types of values. Expected array but received undefined.
//
// 55 | expect(mock.calls).toEqual(expectedCalls[entryName]);
// 56 | } else {
// > 57 | expect(mock.calls).toEqual([]);
// | ^
// 58 | }
// 59 | });
// 60 | };
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment