Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot mock react-native-sound with Jest

When I try to mock react-native-sound with Jest I get the following error:

//PlayerService.js
import Sound from 'react-native-sound';

try {
  console.log('Sound: ' + JSON.stringify(Sound)); //Sound: {}
  _trackPlaying = new Sound('path', Sound.LIBRARY, error => { });
} catch (error) {
  console.log('error: ' + JSON.stringify(error)); //error: {}
}
//PlayerService.tests.js
jest.mock('react-native-sound', () => ({
  Sound: jest.fn((path, type, callback) => {

  })
}));

// package.json

{
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-jest": "^21.2.0",
    "babel-plugin-transform-flow-strip-types": "^6.22.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-flow": "^6.23.0",
    "flow": "^0.2.3",
    "jest": "^21.2.1"
  },
  "jest": {
    "modulePathIgnorePatterns": [
      "__mocks__/"
    ]
  },
  "dependencies": {
    "react-native-sound": "^0.10.4"
  }
}

Alternatively I've tried setting up a manual mock in a separated file (__mock__ folder) with similar luck:

//__mocks__/react-native-sound.js
const Sound = jest.genMockFromModule('react-native-sound');

Sound = (path, type, callback) => {
  console.log("mocked");
}

module.exports = Sound;

//__tests__/PlayerService.tests.js
jest.mock('react-native-sound'); // doesn't work

Any guidance or advice will be greatly appreciated. Thanks much in advance!

like image 715
Facundo La Rocca Avatar asked Oct 17 '22 02:10

Facundo La Rocca


2 Answers

Well, I found the problem in the end.

The way I was trying to make the mock was the problem, so what I did was to to return a new function simulating what I need to mock:

jest.mock('react-native-sound', () => {
  var _filename = null;
  var _basePath = null;

  var SoundMocked = (filename, basePath, onError, options) => {
    _filename = filename;
    _basePath = basePath;
    onError();
  }

  SoundMocked.prototype.filename = () => _filename;
  SoundMocked.prototype.basePath = () => _basePath;
  SoundMocked.prototype.play = function (onEnd) { };
  SoundMocked.prototype.pause = function (callback) { };
  SoundMocked.prototype.stop = function (callback) { };
  SoundMocked.prototype.reset = function () { };
  SoundMocked.prototype.release = function () { };
  SoundMocked.prototype.getDuration = function () { };

  SoundMocked.LIBRARY = 2;

  return SoundMocked;
});
like image 111
Facundo La Rocca Avatar answered Nov 12 '22 11:11

Facundo La Rocca


You have to mock all of the functionality you are using. The mocks you made aren't providing all of the functionality you need. You need to mock the constructor for Sound because you are creating one, you need to mock Sound.LIBRARY because you are using that as well. Also, the mock you need to make is very simple I wouldn't bother using genMockFromModule it's more for extending the module. Just implement a simple mock yourself it will give you more control over what the module actually does. Since you are using babel and ES6 I would just mock this as a class and mock all of the functions you use.

Something like this. . .

// __mocks__/react-native-sound.js

class Sound {
  constructor(path, type, callback) {
    ...
  }

  LIBRARY = 1
}

export Sound;

Then in your test

jest.mock('react-native-sound');

I'm not entirely sure on the syntax for replacing a module in react-native (just make sure your mocks folder is in the same directory as what you are including) so you might also need to specify that you are trying to replace the module in moduleNameMapper like this and then I don't believe you need to mock it in your file. However this will only work if you plan on mocking react-native-sound globally.

"moduleNameMapper": {
  "react-native-sound": "<rootDir>/__mocks__/react-native-sound.js"
}

Now if you have any issues with something being undefined you can add it to your mock class and it will fix the issue.

Note that you can also use es5 syntax to do this if you prefer.

like image 40
Dakota Avatar answered Nov 12 '22 11:11

Dakota