Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot read property '_transformer' of undefined #568

Closed
liangchunn opened this issue Oct 3, 2018 · 14 comments
Closed

Cannot read property '_transformer' of undefined #568

liangchunn opened this issue Oct 3, 2018 · 14 comments

Comments

@liangchunn
Copy link

liangchunn commented Oct 3, 2018

Description

I cannot use ioredis-mock with Jest module mocking, it keeps erroring back the below error. I've tried to pinpoint it down but couldn't find anything. Any ideas?

 FAIL  src/__tests__/Redis.ts
  ● Test suite failed to run

    TypeError: Cannot read property '_transformer' of undefined
    > 1 | const ioredis = require('ioredis-mock')        
        |                                            ^
      2 | module.exports = ioredis

      at Object.<anonymous> (node_modules/ioredis-mock/lib/index.js:148:34)
      at Object.<anonymous> (__mocks__/ioredis.ts:1:44)
      at Object.<anonymous> (src/Redis.ts:35:17)

Source files

/__mocks__/ioredis.ts

const ioredis = require('ioredis-mock')
module.exports = ioredis

/src/__tests__/Redis.ts

import { Redis } from "../Redis";

// uncommenting the line below will require the actual ioredis
// jest.unmock('ioredis')
describe('RedisAdapter', () => {
  it('can get', async () => {
    const redis = new Redis();
    expect(await redis.get('KEY')).toBe(null)
  })
})

/src/Redis.ts

import * as ioredis from 'ioredis'

export class Redis {
    private instance: ioredis.Redis | null
    constructor() {
        this.instance = null
    }
    private async getInstance() {
        if (!this.instance) {
            this.instance = new ioredis({
                host: '127.0.0.1',
                port: 6379,
                retryStrategy: (times) => false
            })
            return this.instance
        } else {
            return this.instance
        }
    }
    public async get(k: string) {
        return this.getInstance().then(instance => instance.get(k))
    }
}

Sample Repo

/~https://github.com/liangchunn/ioredis-transform-error

@stipsan
Copy link
Owner

stipsan commented Oct 6, 2018

Hey!

This is because ioredis-mock is importing some code from ioredis.
Thus for jest mocking to work I recommend you put create another small module that imports ioredis so that you can mock that module by path. Allowing ioredis-mock to import code from ioredis without jest's mocking system intervening 😄

Something like:
/src/__mocks__/RedisClient.ts

const ioredis = require('ioredis-mock')
module.exports = ioredis

/src/__tests__/Redis.ts

import { Redis } from "../Redis";

describe('RedisAdapter', () => {
  it('can get', async () => {
    const redis = new Redis();
    expect(await redis.get('KEY')).toBe(null)
  })
})

/src/RedisClient.ts

import * as ioredis from 'ioredis'

export default redis

/src/Redis.ts

import ioredis from './RedisClient'

export class Redis {
    private instance: ioredis.Redis | null
    constructor() {
        this.instance = null
    }
    private async getInstance() {
        if (!this.instance) {
            this.instance = new ioredis({
                host: '127.0.0.1',
                port: 6379,
                retryStrategy: (times) => false
            })
            return this.instance
        } else {
            return this.instance
        }
    }
    public async get(k: string) {
        return this.getInstance().then(instance => instance.get(k))
    }
}

Hope this helps 😄

@liangchunn
Copy link
Author

@stipsan Thank you so much, it worked! Updating the example repo just in case anybody stumbles across this issue.

@stipsan
Copy link
Owner

stipsan commented Oct 7, 2018

Great to hear! 👍

@feugy
Copy link

feugy commented May 15, 2019

For what it worth, if you don't want to change to introduce this client file, here is what you can do:

    jest.mock('ioredis', () => {
      const Redis = require('ioredis-mock')
      if (typeof Redis === 'object') {
        // the first mock is an ioredis shim because ioredis-mock depends on it
        // /~https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L101-L111
        return {
          Command: { _transformer: { argument: {}, reply: {} } }
        }
      }
      // second mock for our code
      return function(...args) {
        return new Redis(args)
      }
    })

If you need access to the ioredis-mock instance:

    const mocks = { redis: null }
    jest.mock('ioredis', () => {
      const Redis = require('ioredis-mock')
      if (typeof Redis === 'object') {
        // the first mock is an ioredis shim because ioredis-mock depends on it
        // /~https://github.com/stipsan/ioredis-mock/blob/2ba837f07c0723cde993fb8f791a5fcfdabce719/src/index.js#L100-L109
        return {
          Command: { _transformer: { argument: {}, reply: {} } }
        }
      }
      // second mock for our code
      return function(...args) {
        const instance = new Redis(args)
        mocks.redis = instance
        return instance
      }
    })

@stipsan
Copy link
Owner

stipsan commented Jul 30, 2019

Reopening this as it would be really helpful to have this information in the readme 🤔

@stipsan stipsan reopened this Jul 30, 2019
@sjclemmy
Copy link

sjclemmy commented Jan 23, 2020

I'm currently struggling with this, and I just wanted to add my thoughts.
There is another way to add ioredis-mock to jest, like this:

jest.setMock('ioredis', require('ioredis-mock'));
const ioredis = require('ioredis');

This will then mock out any references to ioredis in the codebase.
This is quite a nice way of doing this, however this is problematic because a Redis instance is not created in the test, but in the codebase, and I can't see how to add test data to the mock redis instance.
It would be nice if it was this simple to mock ioredis, and there was an easy way to add data at this point so that when the Redis instance is instantiated the data is then applied. I have no idea how to achieve this.

@heygrady
Copy link

This seemed to work:

in __mocks__/ioredis.js

const ioredisMock = require('ioredis-mock')

jest.mock('ioredis', () => {
  const Redis = require('ioredis-mock')
  if (typeof Redis === 'object') {
    // the first mock is an ioredis shim because ioredis-mock depends on it
    // /~https://github.com/stipsan/ioredis-mock/blob/master/src/index.js#L101-L111
    return {
      Command: { _transformer: { argument: {}, reply: {} } },
    }
  }
  // second mock for our code
  return Redis
})

module.exports = ioredisMock

@santiagofm
Copy link

None of the above worked for us, the following did:

// On the jest setup file: __test__/setup/index.ts

const ioredisMock = require('ioredis-mock')
jest.setMock('ioredis', ioredisMock)

@rhysawilliams2010
Copy link

@santiagofm solution worked for me.
I had to make sure that I put it in a script which is configured in my jest.config.js
setupFiles NOT setupFilesAfterEnv.

@buildscientist
Copy link

@sjclemmy Your method of mocking Redis allows you to write directly to Redis using the mock client within the test itself. For example if you're using Jest you create a test harness to preload data before each test using all the standard ioredis methods and the data would be written in-memory.

I prefer this over passing a data object to the RedisMock constructor. In my service/application I have specialized DAOs/DTOs to read/write data to Redis. Not sure what your application does but if you follow this pattern and create your own set of DAOs and DTO's then you can very easily test by using ioredis-mock to mock all those calls out to Redis.

@sibelius
Copy link
Contributor

what is the best way to clean up redis data among tests?

@buildscientist
Copy link

@sibelius I'd recommend doing a FLUSHALL between each test depending on your requirements.

@stipsan
Copy link
Owner

stipsan commented Mar 23, 2021

Starting in v5.4 Jest module mocking has first class support:

jest.mock('ioredis', () => require('ioredis-mock/jest'));

@stipsan stipsan closed this as completed Mar 23, 2021
@boredland
Copy link

@stipsan even with that I still get

TypeError: Cannot read properties of undefined (reading '_transformer')

      at Object.<anonymous> (../node_modules/ioredis-mock/lib/command.js:69:34)
      at Object.<anonymous> (../node_modules/ioredis-mock/lib/commands/defineCommand.js:16:39)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants