Jest: mock.instances[0] is returning `mockConstructor {}` instead of actual object

Created on 22 Feb 2017  路  6Comments  路  Source: facebook/jest

Do you want to request a feature or report a bug?
report a bug

What is the current behavior?
_repl.it demo: https://repl.it/FuQO/0_

let MyMock = jest.fn();
MyMock.mockImplementation(() => ({
  name: 'test',
}));

describe('some tests', () => {
  it('run the instances check', () => {
    let newMock = new MyMock();
    expect(MyMock.mock.instances.length).toBe(1);
    expect(MyMock.mock.instances[0]).toBe(newMock);
  });
});

```
- Expected
+ Received

-Object {
-  "name": "test",
-}
+mockConstructor {}
**What is the expected behavior?** (Copied from API docs)
```javascript
// had a `name` property whose value was set to 'test'
expect(someMockFunction.mock.instances[0].name).toEqual('test'); // success

More Info
This API is documented but tests only run with empty functions. I have written tests here which are failing due to this bug.
https://github.com/facebook/jest/compare/master...vibhavsinha:master

Please provide your exact Jest configuration and mention your Jest, node, yarn/npm version and operating system.

  • jest: v19.0.0
  • node: v6.9.1
  • yarn: 0.20.3
  • OS: Ubuntu 16.10 Linux 4.8.0-37-generic GNU/Linux

Most helpful comment

Here's my work around for manually mocking ES6 classes.
It also work with functions. It does not align with Jest documentation

Note that it is a function() that I use for mockImplementation()

let MyMock = jest.fn();
MyMock.mockImplementation(function() {
  this.name = 'test'
  this.foo = jest.fn(() => {
    return 'bar'
  })
});

describe('some tests', () => {
  it('run the instances check', () => {
    let newMock = new MyMock();
    newMock.foo()
    expect(MyMock.mock.instances.length).toBe(1);
    expect(MyMock.mock.instances[0]).toBe(newMock);
    expect(MyMock.mock.instances[0].foo).toHaveBeenCalled()
  });
});

All 6 comments

This is expected. Mocking works by subclassing things in Jest.

@cpojer can you please point to any code example that shows how to do this?

Here's my work around for manually mocking ES6 classes.
It also work with functions. It does not align with Jest documentation

Note that it is a function() that I use for mockImplementation()

let MyMock = jest.fn();
MyMock.mockImplementation(function() {
  this.name = 'test'
  this.foo = jest.fn(() => {
    return 'bar'
  })
});

describe('some tests', () => {
  it('run the instances check', () => {
    let newMock = new MyMock();
    newMock.foo()
    expect(MyMock.mock.instances.length).toBe(1);
    expect(MyMock.mock.instances[0]).toBe(newMock);
    expect(MyMock.mock.instances[0].foo).toHaveBeenCalled()
  });
});

If anyone's curious, here's what I ended up using in my TypeScript project:

Let's say that I want to test this function:

export async function list(DomainName: string, next?: string): Promise<MyModelListResponse> {
  const simpledb = new SimpleDB();
  const params: SimpleDB.SelectRequest = {
    SelectExpression: `select * from \`${DomainName}\``,
    ...next ? { NextToken: next } : {}
  };
  console.log(`Querying SimpleDB: ${JSON.stringify(params, null, 2)}`);
  const resp = await simpledb.select(params).promise();
  return {
    results: resp.Items.map(
      item => item.Attributes.reduce(
        (obj, attr) => Object.assign(obj, {[attr.Name]: attr.Value}),
        {}
      ) as MyModel
    ),
    nextToken: resp.NextToken
  };
}

My test:

import { SimpleDB } from 'aws-sdk';

import * as MyDbInterface from './db';

jest.mock('aws-sdk');
const MockSimpleDB = SimpleDB as any as jest.MockInstance<SimpleDB>;

// Create mock object that simulates the response of `new SimpleDB().someMethod(args).promise()`
const mockAwsMethodPromiseObject = (prototype: {[method: string]: any}): {[key: string]: jest.Mock} =>
  Object.assign({},
    ...Object.entries(prototype).map(([method, response]) => ({
      [method]: jest.fn(() => ({
        promise: jest.fn(() => response)
      }))
    }))
  )
;

describe('DB list', () => {

  it('should list returned models', async () => {
    const response: SimpleDB.SelectResult = {
      Items: [{Name: 'foo', Attributes: [{Name: 'id', Value: 'foo'}]}]
    };
    const mock = mockAwsMethodPromiseObject({select: Promise.resolve(response)});
    MockSimpleDB.mockImplementation(() => mock);
    const models = [{'id': 'foo'}];

    expect(
      await MyDbInterface.list('testDomain')
    ).toEqual({
      nextToken: undefined,
      results: models
    });
    expect(MockSimpleDB).toHaveBeenCalledTimes(1); // Instantiated
    expect(mock.select).toHaveBeenCalledTimes(1);
    expect(mock.select).toHaveBeenCalledWith({SelectExpression: "select * from `testDomain`"});
  });
});

I'm definitely not an expert regarding testing, Jest, or TypeScript, however this seems to work for me.

Hi guys,

If you would like to pass typescript class as mocked class, this is my typescript version of @tinyroy 's workaround.
The mock (./__mocks__/PluginManager.ts)

class PluginManagerMockImpl {
    run = jest.fn(() => {
        console.log("I am mocked");
    })
}

export const PluginManager = jest.fn().mockImplementation(PluginManagerMockImpl as any);

The test (./TestClass.test.ts)):

    test("will run", async () => {
        const pluginManager = new (PluginManager as any)()
        pluginManager.run();
        const pluginManagerMock = mocked(PluginManager);
        const runMocked = mocked(pluginManagerMock.mock.instances[0].run);
        expect(runMocked).toBeCalledTimes(1);
    });
Was this page helpful?
0 / 5 - 0 ratings