expressunit-testingtestingjestjsexpress-validator

Jest.spyOn return an error -"Cannot use spyOn on a primitive value; undefined given" Express.js package "express-validator"


I use Express.js, to validate requests, I use a package express-validator. And I want to test the controller:

import { validationResult } from "express-validator";

class RegistrationController {
  async registration(req, res, next): Promise<Response> {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return next("Houston, we have a problem");
      }
      return res.status(200).json(true);
    } catch (e) {
      next(e);
    }
  }
}

export = new RegistrationController();

This is my test:

import RegistrationController from "./registration.controller";
import validator from "express-validator";

describe("registration", () => {
  const { registration } = RegistrationController;

  let req = {},
    res = {},
    next = jest.fn();

  it('If errors occur during the request validation process, next("Houston, we have a problem") should be called', async () => {
    const validationResultMock = {
      isEmpty: () => false,
    };

    jest
      .spyOn(validator, "validationResult")
      //@ts-ignore
      .mockImplementation(() => validationResultMock);

    await registration(req, res, next);

    expect(next).toBeCalledWith("Houston, we have a problem");
  });
});

During the test, an error falls:

enter image description here

What does this error mean? Am I spying on the package incorrectly? How do I fix it so that the test passes?


Solution

  • In short, I have not found an answer on the Internet how to spy on express-validator, no one does this, they use other ways of testing controllers. Why the error appears, how to fix it, how I tested my controller - I will write further...

    That's how I can answer my questions:

    1. What does this error mean? Am I spying on the package incorrectly?

    I am correctly spying on the "validationResult" method in the "express-validator" package, the problem is that "validationResult" is not a method of the package. If you go to the address where the "express-validator" package is imported from, you can see:

    enter image description here

    That "validationResult" is the address of the file that is exported from the "express-validator" package. This is firstly, and secondly, if you look at the file itself at the address './validation-result', that export declare class Result<T = any> class is located here, which has the isEmpty() method

    enter image description here

    Most likely, this method is used in the string:

    errors.isEmpty()
    

    Therefore, we can conclude that the error appears due to the special structure of the "express-validator" package when I try to follow a method "validationResult" that does not exist.

    2. How do I fix it so that the test passes?

    The main task of my test was to check this part of the code:

    const errors = validationResult(req);
          if (!errors.isEmpty()) {
            return next("Houston, we have a problem");
          }
    

    To be sure, if validation went wrong, the function call will work: next("Houston, we have a problem")

    There are quite a lot of ways to do this on the Internet, but they all talked about the same thing - you need to take the application and simulate an incorrect request, then the request will be processed and the controller will process it according to the instructions. And at the test level, you will make a comparison with the expected result. The most popular library for query simulation that I found was "supertest", which I used in my new test.

    For example, this is how my routes look like:

    import express from "express";
    import { body } from "express-validator";
    
    // CONTROLLERS
    import registrationController from "../controllers/registration.controller";
    
    const router = express.Router();
    
    // REGISTRATION
    router.post(
      "/registration",
      body("email").isEmail(),
      body("password").isLength({ min: 3, max: 32 }),
      registrationController.registration
    );
    
    export = router;
    

    And this is my new test:

    import express from "express";
    import request from "supertest";
    
    describe("RegistrationController", () => {
      describe("registration", () => {
       const app = express();
        it("If an error occurs during the execution of the method, the next('Houston, we have a problem') call should occur", async () => {
          const next = jest.fn();
    
          try {
            // Passing an empty request to the application that will not pass validation
            await request(app).post("/registration");
          } catch (e) {
            // We compare the result of processing the request by the controller with the expected result
            expect(next).toHaveBeenCalledWith("Houston, we have a problem");
          }
        });
      });
    });
    

    We are glad that everything works for us

    enter image description here

    If you know how to properly monitor the "ValidationResult" method from the "express-validator" package . in order not to get errors, please share your way. I have not found such a method on the Internet, I will be very grateful to you