Search code examples
node.jsjestjsts-jest

My jest test is failing for my controller with a json method missing error


I have a controller that registers a user, however, testing it does not work. I am using Jest in Node version 16. Here is the controller: I get the following error TypeError: response.status(...).json is not a function

I did implement the json method of the object so I don't know why I'm getting this error


        class AuthenticationController {

        registerNewUser = async(request:Request, response:Response, next:NextFunction) =>{
            const username = request.body.username
            const password = request.body.password
            if(!username || !password){
                
                response.status(400).json({success:false, 'no username or password'}) 
            }
            const query = {username: username}
            const user = await User.findOne(query)
            if(user){
               response.status(400).json({message: 'User exists'})
            } 
            const hashUtility = new HashUtil()
            const {hash}  = hashUtility.createHash(password)
            const createQuery = (username: username,hash:hash)
            await User.create( createQuery)
            // TypeError: response.status(...).json is not a function
            // is now thrown here
            response.status(200).json(createQuery)   
        }
    
    
    }

    export {AuthenticationController}


Here is my test class


    describe('UserAuthController test suite', ()=>{
            let authController:AuthenticationController 
           
            beforeEach(()=>{
               authController  = new AuthenticationController()
            })
           it('should successfully register a new user ', async ()=>{
             let user  = new User({
                username: 'user@gmail.com',
                hash: 'ddh44500000',
              })
           
            let mockRequest = {
                body: {
                username: 'user@gmail.com',
                password: 'mypassword'
               }
             }
            let responseObject = {};
            let mockResponse: Partial<Response> = {
              statusCode: 200,
              status: jest.fn().mockImplementation(()=>200),
              json: jest.fn().mockImplementation((result) =>{
                responseObject = result
              })
          }
             UserConfig.findOne = 
                 jest.fn().mockImplementation((query)=>{
                   return null
             })
             let next = jest.fn()
             let hashUtility = new HashUtil()
             jest.spyOn(hashUtility, 'createHash').mockImplementationOnce((password) =>{
               return {hash: 'ddh44500000'}
              })
            await authController.registerNewUser(mockRequest as Request, mockResponse as 
            Response, next)
            console.log(`ResponseObjeect ${responseObject}`)
       })
     })

I don't know what I'm missing in my test code. Please help


Solution

  • The problem is that you're not creating your mockResponse object properly.

    You're response object's methods should support method chaining as described in your code:

     response.status(200).json({ message: "msg" });
    

    In order to be able to call .json({ message: "msg" }) the previous status method must return this (the response object).

    But in you're test code you've the following:

    let mockResponse: Partial<Response> = {
      statusCode: 200,
      status: jest.fn().mockImplementation(() => 200),
      json: jest.fn().mockImplementation((result) => {
        responseObject = result;
      }),
    };
    

    So, when you pass this mock response object to the middle-ware and it calls response.status(400) it returns 200 (according to your jest.fn().mockImplementation(() => 200)).

    Then it chains the .json({}) call but as the returned value from previous call was 200, so (200).json({...}) throws an error:

    TypeError: response.status(...).json is not a function

    This is an easy fix, you just need to return the mockResponse object from each mock methods, like the following snippet:

    let responseObject = {};
    let mockResponse = {
      statusCode: 200,
      status: jest.fn().mockImplementation((status) => mockResponse),
      json: jest.fn().mockImplementation((result) => {
        responseObject = result;
        return mockResponse
      }),
    };