Instantly share code, notes, and snippets.

Embed
What would you like to do?
Unit test React Native with Mocha
--compilers js:./test/support/compiler
--require ./test/support/init
/* eslint-env node, mocha */
import NoteEntryScreen from '../src/containers/screens/NoteEntryScreen';
import common from './support/common';
const {createRenderer, React, expect, MockComponents} = common;
function setup() {
const props = {};
const renderer = createRenderer();
renderer.render(<NoteEntryScreen {...props} />);
const output = renderer.getRenderOutput();
return {
props,
output,
renderer,
};
}
describe('NoteEntryScreen', () => {
it('should render a scrollview', () => {
const { output } = setup();
expect(output.type.displayName).to.equal(MockComponents.ScrollView.displayName);
});
});
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import {MockComponents} from './mocks/react-native';
chai.use(chaiAsPromised);
export default {
expect: chai.expect,
createRenderer: ReactTestUtils.createRenderer,
React,
MockComponents,
};
var fs = require('fs');
var path = require('path');
var babel = require('babel-core');
var origJs = require.extensions['.js'];
require.extensions['.js'] = function (module, fileName) {
var output;
if (fileName === '/app/node_modules/react-native/Libraries/react-native/react-native.js') {
fileName = path.resolve('./test/support/mocks/react-native.js');
}
if (fileName.indexOf('node_modules/') >= 0) {
return (origJs || require.extensions['.js'])(module, fileName);
}
var src = fs.readFileSync(fileName, 'utf8');
output = babel.transform(src, {
filename: fileName,
sourceFileName: fileName,
//keep below in sync with babelrc
"retainLines": true,
"compact": true,
"comments": false,
"plugins": [
"syntax-async-functions",
"syntax-class-properties",
"syntax-trailing-function-commas",
"transform-es2015-arrow-functions",
"transform-es2015-block-scoping",
"transform-es2015-classes",
"transform-es2015-computed-properties",
"transform-es2015-constants",
"transform-es2015-destructuring",
["transform-es2015-modules-commonjs", {"strict": false, "allowTopLevelThis": true}],
"transform-es2015-parameters",
"transform-es2015-shorthand-properties",
"transform-es2015-spread",
"transform-es2015-template-literals",
"transform-class-properties",
"transform-flow-strip-types",
"transform-object-assign",
"transform-object-rest-spread",
"transform-react-display-name",
"transform-react-jsx",
"transform-regenerator"
],
"sourceMaps": false
}).code;
return module._compile(output, fileName);
};
import React from 'react';
function mockComponent(type) {
const Component = React.createClass({
displayName: type,
propTypes: { children: React.PropTypes.node },
render() { return React.createElement(React.DOM.div, this.props, this.props.children); },
});
return Component;
}
const componentsToMock = [
'View',
'Text',
'Component',
'ScrollView',
'TextInput',
];
export const MockComponents = componentsToMock.reduce((agg, type) => {
agg[type] = mockComponent(type);
return agg;
}, {});
export default {
...React,
...MockComponents,
StyleSheet: {
create: (ss) => ss,
},
PropTypes: React.PropTypes,
};
@jmreidy

This comment has been minimized.

Copy link
Owner Author

jmreidy commented Nov 12, 2015

Getting React Native components unit tested with Mocha.

In a normal setup, requiring a component file in a unit test will import React from react-native, which will start throwing errors immediately. To work around these errors, we have to swap out react-native wholesale with vanilla React, and then mock out the react-native components. (Which is fine, since in a unit test we should only care about the system under test anyway).

We can easily witch out react-native without anything like proxyquire by using a custom compiler, which just switches out the import.

Then it's just normal unit testing with mocha!

@jhabdas

This comment has been minimized.

Copy link

jhabdas commented Nov 26, 2015

@jmreidy any chance you've run across any RN seed applications employing this technique? I'm wary about using Jest to unit test my RN applications, and would like to go with Mocha if possible if the library substitution method pans out.

@IanVS

This comment has been minimized.

Copy link

IanVS commented Dec 1, 2015

Thanks for putting this together, @jmreidy. I'm giving it a shot now. Do you have anything special in your ./test/support/init?

@Andrewpk

This comment has been minimized.

Copy link

Andrewpk commented Dec 5, 2015

This is awesome @jmreidy. Are there any additional steps to getting this to work that may have been left out?
Currently I'm having trouble getting the tests to run due to RN requires:

Error: Cannot find module 'ActivityIndicatorIOS'
@jrichardlai

This comment has been minimized.

Copy link

jrichardlai commented Dec 11, 2015

@Andrewpk you want to make sure the path is correct in compiler.js I replaced it with fileName.match('/node_modules/react-native/Libraries/react-native/react-native.js')

@miquelbeltran

This comment has been minimized.

Copy link

miquelbeltran commented Feb 27, 2016

Hi @jmreidy, any change you can upload a full repo with the working example? I am following your code examples but I am stuck with an issue I don't know how to solve when running mocha.

I'll leave here the error stack trace just in case you have any idea. Many thanks in advance!

 mocha
/home/miquel/dev/github/react-app-test-2/AppTest2/app/scroller-item.js:1
(function (exports, require, module, __filename, __dirname) { 'use strict';Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if("value" in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};}();var _reactNative=require('react-native');var _reactNative2=_interopRequireDefault(_reactNative);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class 

TypeError: Super expression must either be null or a function, not undefined
    at _inherits (/home/miquel/dev/github/react-app-test-2/AppTest2/app/scroller-item.js:1:1287)
    at /home/miquel/dev/github/react-app-test-2/AppTest2/app/scroller-item.js:11:35
    at Object.<anonymous> (/home/miquel/dev/github/react-app-test-2/AppTest2/app/scroller-item.js:21:75)
    at Module._compile (module.js:413:34)
    at Object.require.extensions..js (/home/miquel/dev/github/react-app-test-2/AppTest2/test/support/compiler.js:51:17)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at Object.<anonymous> (/home/miquel/dev/github/react-app-test-2/AppTest2/test/scroller.test.js:1:81)
    at Module._compile (module.js:413:34)
    at Object.require.extensions..js (/home/miquel/dev/github/react-app-test-2/AppTest2/test/support/compiler.js:51:17)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at /usr/lib/node_modules/mocha/lib/mocha.js:219:27
    at Array.forEach (native)
    at Mocha.loadFiles (/usr/lib/node_modules/mocha/lib/mocha.js:216:14)
    at Mocha.run (/usr/lib/node_modules/mocha/lib/mocha.js:468:10)
    at Object.<anonymous> (/usr/lib/node_modules/mocha/bin/_mocha:403:18)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Function.Module.runMain (module.js:447:10)
    at startup (node.js:141:18)
    at node.js:933:3
@larubbio

This comment has been minimized.

Copy link

larubbio commented Feb 28, 2016

@miquelbeltran I'm running into the same issue you are. Also is there anyway to use the existing .bablerc instead of duplicating it in code?

@larubbio

This comment has been minimized.

Copy link

larubbio commented Feb 28, 2016

@miquelbeltran I had to modify the mock to get passed the TypeError

var ReactNative = {
  ...React,
  ...MockComponents,
  StyleSheet: {
    create: (ss) => ss,
  },
  PropTypes: React.PropTypes,
};

module.exports = ReactNative;

I think the error had to do with Component not being defined so when babel tried to transpile a module that extends it you get the error. We might be on a new version of some package that requires the different export syntax.

http://stackoverflow.com/questions/30116430/reactjs-giving-error-uncaught-typeerror-super-expression-must-either-be-null-or

I've also had to add mocks for at least one other module (react-native-router-flux) to get my tests to "run".

@sstur

This comment has been minimized.

Copy link

sstur commented Mar 2, 2016

I also got this working with some effort and modifications to the above files. Will post my solution...

@cornedor

This comment has been minimized.

Copy link

cornedor commented Apr 12, 2016

You could use the react-native preset so you won't have to keep the transforms in sync
https://gist.github.com/cornedor/37ab65a3a505797b01c2e78156d5cf12/revisions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment