Search code examples
jestjsjest-fetch-mock

Trying to access mockedFunction.mock.instaces property gives undefined in Jest


I want to mock a constructor function named Dog

Dog = jest.fn(()=>{
    return{
        name:"spike",
        bark:function(){
            return "bhow " +this.name;
        }
    }
})

function foo(){
   const d = new Dog();
   return d.bark();
}




test("testing foo",()=>{
    const result = foo();
    expect(Dog).toHaveBeenCalledTimes(1);
    expect(result).toBe("bhow spike");

    expect(Dog.mock.instances.length).toBe(1);

    expect(Dog.mock.instances[0].name).toBe("spike");
    //this test failed with expected spike received undefined
});

but the expect(Dog.mock.instances[0].name).toBe("spike"); fails with expected spike received undefined

jest version 24.8.0 node version 10.15.0


Solution

  • When you call a function with the new operator, a new object is created and passed as the execution context (aka this) to the function. This object is then implicitly returned if the function does not return anything explicitly. You can have a look at the detailed explanation.

    Also, take into account that an arrow function can never be used as a constructor.

    From Jest documentation for mock functions:

    mockFn.mock.instances

    An array that contains all the object instances that have been instantiated from this mock function using new.

    So, Jest mock functions are storing in the instances attribute the list of object instances that are being passed to the function (the newly created object that is passed as this to the function) every time you call it with the new operator.

    But your constructor is not using the this object, so it remains empty. That is why when you check Dog.mock.instances[0].name you are getting undefined. If you change slightly your constructor to assign the name attribute to the this object you can see that your test passes:

    Dog = jest.fn(function() {
        this.name = "spike";
        return{
            name:"spike",
            bark:function(){
                return "bhow " +this.name;
            }
        }
    })
    

    Returning explicitly an object from a constructor function as you are doing is rarely used. The most usual way to define a constructor is to assign its properties to the this object. So, a solution to your problem would be to change your constructor function to:

    Dog = jest.fn(function() {
        this.name = "spike";
        this.bark = function(){
            return "bhow " +this.name;
        }
    })
    

    Another solution, if you don't want to change the definition of your constructor function, would be to use the results attribute of the mock function in your test:

    test("testing foo",()=>{
        const result = foo();
        expect(Dog).toHaveBeenCalledTimes(1);
        expect(result).toBe("bhow spike");
    
        expect(Dog.mock.instances.length).toBe(1);
    
        expect(Dog.mock.results[0].value.name).toBe("spike");
    });