Search code examples
typescriptunit-testingjestjsts-jestts-mockito

Issues mocking objects in Typescript with ts-mockito


When I try to use mockito-ts to mock the return value of member functions on instances of mocked objects, I get that the member function is not a function.

TypeError: Cannot read properties of null (reading 'methodStubCollection')

When I ran this in the larger project I am working on, I had a different message indicating a similar issue. Basically, mockObject.mockMethod is not a function.

this question and others emphasize that the instance call is critical, and I think I've done that right. But I still see issues.

Here is the minimal reproducible code example:

package.json:

{
  "name": "dwf_backend",
  "version": "1.0.0",
  "description": "Server for DrawWithFriends app",
  "main": "index.js",
  "scripts": {
    "test": "jest --coverage",
    "dev": "concurrently \"npx tsc --watch\" \"nodemon -q dist/server.js\"",
    "start": "ts-node src/index.ts"
  },
  "author": "TJ",
  "license": "ISC",
  "devDependencies": {
    "@types/jest": "^29.5.1",
    "jest": "^29.5.0",
    "ts-jest": "^29.1.0",
    "ts-mockito": "^2.6.1"
  }
}

test/mockito_test.test.ts:

import { mock, instance, anything, when } from 'ts-mockito'


class B {
    public doTheThing(str: string): number {
        return str.length;
    }
}

class A {
    private readonly b: B;
    constructor(b: B) {
        this.b = b;
    }

    public doTheMainThing() {
        this.b.doTheThing('strrr');
    }
}

describe('test cases', () => {
    it('passes', () => {
    });

    it('mocks', () => {
        const mockedB: B = mock(B);

        const a = new A(instance(mockedB));
        a.doTheMainThing();
    });

    it('controls the mocked method', () => {
        const mockedB: B = mock(B);

        const instanceB: B = instance(mockedB);
        console.log(`instanceB is: ${JSON.stringify(instanceB)}`);

        when(instanceB.doTheThing(anything())).thenReturn(4);

        const a = new A(instanceB);
        a.doTheMainThing();
    });
});

run with npm run test


Solution

  • From Stubbing method calls doc, we should pass the mocked object returned by the mock function rather than the instance object returned by the instance function to the when function to stub method and its return value.

    It should be:

    const mockedB: B = mock(B);
    const instanceB: B = instance(mockedB);
    when(mockedB.doTheThing(anything())).thenReturn(4);
    

    NOT

    const mockedB: B = mock(B);
    const instanceB: B = instance(mockedB);
    when(instanceB.doTheThing(anything())).thenReturn(4);
    

    An working example:

    import { mock, instance, anything, when } from 'ts-mockito'
    
    class B {
      public doTheThing(str: string): number {
        return str.length;
      }
    }
    
    class A {
      private readonly b: B;
      constructor(b: B) {
        this.b = b;
      }
    
      public doTheMainThing() {
        return this.b.doTheThing('strrr');
      }
    }
    
    describe('test cases', () => {
      it('controls the mocked method', () => {
        const mockedB: B = mock(B);
        const instanceB: B = instance(mockedB);
        when(mockedB.doTheThing(anything())).thenReturn(4);
    
        const a = new A(instanceB);
        const actual = a.doTheMainThing();
        expect(actual).toBe(4);
      });
    });
    

    Test result:

     PASS  stackoverflow/76133872/index.test.ts
      test cases
        ✓ controls the mocked method (2 ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        0.665 s, estimated 1 s