javascriptjestjsts-jestsalesforce-lightninglwc

How to reduce duplicated code in tests by extracting parts of it in a function or by using it.each in Jest


I was asked to reduce duplication in my tests by using it.each or creating a function. I am not sure how to go about this. I have other functions in the test suite that are not expecting errors. I am assuming I can put the two duplicated assertions below in a helper function but if you have a better suggestion, let me know. Thank you.

expect(<jest.Mock>fetch).not.toHaveBeenCalled();
expect(e).toBeInstanceOf(TypeError);

test file:

it('should throw if no args were passed', async () => {
            expect.assertions(3);
            return readPricing().catch((e: Error) => {
                expect(<jest.Mock>fetch).not.toHaveBeenCalled();
                expect(e).toBeInstanceOf(TypeError);
                expect(e.message).toBe(
                    `The 'products' information is required to successfully be able to execute this request.`
                );
            });
        });

        it('should throw if no product IDs were passed', () => {
            expect.assertions(3);
            const noIdData = {
                products: [{ salesPrice: '60.00' }, { salesPrice: '80.00' }],
            };
            // @ts-expect-error
            return readPricing(noIdData).catch((e: Error) => {
                expect(<jest.Mock>fetch).not.toHaveBeenCalled();
                expect(e).toBeInstanceOf(TypeError);
                expect(e.message).toBe(`The 'productId' is required to successfully be able to execute this request.`);
            });
        });

        it('should throw if an empty array of products was passed', () => {
            expect.assertions(3);
            const noIdData = { products: [] };
            return readPricing(noIdData).catch((e: Error) => {
                expect(<jest.Mock>fetch).not.toHaveBeenCalled();
                expect(e).toBeInstanceOf(TypeError);
                expect(e.message).toBe(
                    `The 'products' information is required to successfully be able to execute this request.`
                );
            });
        });

I tried using it.each but was unsuccessfully able to set it up and I am not sure it is possible with the given inputs.


Solution

  • it.each() should do the trick, you can achieve something like:

    describe("temp", () => {
      const productInfoMissingErr = `The 'products' information is required to successfully be able to execute this request.`;
      const productIdMissingErr = `The 'products' information is required to successfully be able to execute this request.`;
      const noIdData = {
        products: [{ salesPrice: "60.00" }, { salesPrice: "80.00" }],
      };
    
      it.each([
        ["no args were passed", undefined, productInfoMissingErr],
        ["no product IDs were passed", noIdData, productInfoMissingErr],
        [
          "an empty array of products was passed",
          { products: [] },
          productIdMissingErr,
        ],
      ])("should throw if %s", (caseTitle, input, errMsg) => {
        return readPricing().catch((e: Error) => {
          expect.assertions(3);
          expect(<jest.Mock>fetch).not.toHaveBeenCalled();
          expect(e).toBeInstanceOf(TypeError);
          expect(e.message).toBe(errMsg);
        });
      });
    });
    

    Which should render the same messages:

      temp
        ✓ should throw if no args were passed (1 ms)
        ✓ should throw if no product IDs were passed
        ✓ should throw if an empty array of products was passed (1 ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       3 passed, 3 total
    Snapshots:   0 total
    Time:        0.226 s, estimated 1 s
    Ran all test suites.