So the last two Unit tests fail - the reason I suspect is that the subcomponents have not been rendered since the App is waiting for a subscription to be ready. How to make the Unit tests wait for the subscription to be ready to allow testing?:
Unit test code:
/* global describe it beforeEach */
import React from 'react';
import { assert } from 'chai';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import App from './App';
configure({ adapter: new Adapter() });
describe('App component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />);
});
it('should render without blowing up', () => {
assert.equal(1, wrapper.length);
});
it('should have one HelloWorld component', () => {
assert.equal(1, wrapper.find('HelloWorld').length);
});
it('should have one ClickMe component', () => {
assert.equal(1, wrapper.find('ClickMe').length);
});
});
App component code:
import { Meteor } from 'meteor/meteor';
import React from 'react';
import Counts from '../../api/counts';
import HelloWorld from './HelloWorld';
import ClickMe from './ClickMe';
/**
* Top level component.
*/
class App extends React.Component {
/**
* Constructor for App component.
* @param {Object} props React properties.
*/
constructor(props) {
super(props);
this.handleOnClick = this.handleOnClick.bind(this);
}
/**
* React life cycle method to handle collection subscription.
*/
componentDidMount() {
this.sub = Meteor.subscribe('counts', () => {
const record = Counts.findOne();
const count = record ? record.count : 0;
this.setState({ count });
});
}
/**
* Event handler for button onClick events.
*/
handleOnClick() {
let newCount = 1;
const record = Counts.findOne();
if (record) {
Meteor.call('counts.increment', record._id);
newCount = record.count + 1;
} else {
Meteor.call('counts.insert', newCount);
}
this.setState({ count: newCount });
}
/**
* Component render method.
* @return {Object} JSX transpiled code.
*/
render() {
if (!this.state) {
return <div>Loading ...</div>;
}
return (
<div className="app">
<HelloWorld />
<ClickMe count={this.state.count} handleOnClick={this.handleOnClick} />
</div>
);
}
}
export default App;
Have you done the Meteor React tutorial? I would strongly recommend rethinking your current approach and using the best practices laid out there. For example, your subscribe isn’t reactive and you’re not stopping the subscription when the component unmounts.
I have done the tutorial a while back - however - now I got annoyed with the meteor-react-data withTracker component wrapper. It’s syntax is unclean (IMHO) and it is unclear what it does exactly - and it breaks the unit tests…
The Unit test stacktrace for this assertion assert.equal(1, wrapper.find('HelloWorld').length); is this:
AssertionError: expected 1 to equal 0
at Context.it (app/app.js?hash=0e2d748afc088ec653ed9179f1280dd6996b6ded:155:12)
at callFn (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:4623:21)
at Test.Runnable.run (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:4615:7)
at Runner.runTest (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:5151:10)
at http://localhost:3100/packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:5269:12
at next (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:5065:14)
at http://localhost:3100/packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:5075:7
at next (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:4999:14)
at http://localhost:3100/packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:5038:7
at done (packages/meteortesting_mocha-core.js?hash=ae586378424da85180afe3acf5f3fdc0f48b54b1:4570:5)
=> Meteor server restarted
I20181107-10:34:30.225(1)? 1) "before each" hook: wrappedFunction for "should render without blowing up"
I20181107-10:34:30.225(1)?
I20181107-10:34:30.226(1)? ClickMe component
I20181107-10:34:30.226(1)? ✓ should render without blowing up
I20181107-10:34:30.226(1)?
I20181107-10:34:30.226(1)? HelloWorld component
I20181107-10:34:30.226(1)? ✓ should render without blowing up
I20181107-10:34:30.226(1)?
I20181107-10:34:30.226(1)?
I20181107-10:34:30.226(1)? 2 passing (36ms)
I20181107-10:34:30.227(1)? 1 failing
I20181107-10:34:30.227(1)?
I20181107-10:34:30.227(1)? 1) App component
I20181107-10:34:30.227(1)? "before each" hook: wrappedFunction for "should render without blowing up":
I20181107-10:34:30.227(1)? TypeError: Meteor.subscribe is not a function
I20181107-10:34:30.227(1)? at App.jsx.module.exportDefault.withTracker (imports/ui/components/App.jsx:39:10)
I20181107-10:34:30.227(1)? at ReactMeteorDataComponent.getMeteorData (packages/react-meteor-data/ReactMeteorData.jsx:181:16)
I20181107-10:34:30.227(1)? at MeteorDataManager.calculateData (packages/react-meteor-data/ReactMeteorData.jsx:34:24)
I20181107-10:34:30.227(1)? at ReactMeteorDataComponent.componentWillMount (packages/react-meteor-data/ReactMeteorData.jsx:130:45)
I20181107-10:34:30.228(1)? at ReactShallowRenderer._mountClassComponent (/Users/iehdk/install/src/meteor-react-app/node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:487:26)
I20181107-10:34:30.228(1)? at ReactShallowRenderer.render (/Users/iehdk/install/src/meteor-react-app/node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:448:14)
I20181107-10:34:30.228(1)? at /Users/iehdk/install/src/meteor-react-app/node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:476:35
I20181107-10:34:30.228(1)? at withSetStateAllowed (/Users/iehdk/install/src/meteor-react-app/node_modules/enzyme-adapter-utils/build/Utils.js:132:16)
I20181107-10:34:30.228(1)? at Object.render (/Users/iehdk/install/src/meteor-react-app/node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:475:68)
I20181107-10:34:30.228(1)? at new ShallowWrapper (/Users/iehdk/install/src/meteor-react-app/node_modules/enzyme/build/ShallowWrapper.js:204:22)
I20181107-10:34:30.228(1)? at shallow (/Users/iehdk/install/src/meteor-react-app/node_modules/enzyme/build/shallow.js:21:10)
I20181107-10:34:30.228(1)? at Hook.beforeEach (imports/ui/components/App.test.jsx:15:15)
I20181107-10:34:30.228(1)? at run (packages/meteortesting:mocha-core/server.js:34:29)
I20181107-10:34:30.228(1)?
I suspect that the unit tests aren’t properly emulating the client environment. Nothing about your testing code implies it’s connecting to the meteor server at all, so even if Meteor.subscribe were defined, it doesn’t know what to connect to.
Have you already read what the guide has to say on testing react components?
import { Meteor } from 'meteor/meteor';
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import PropTypes from 'prop-types';
import MongoCounts from '../../api/MongoCounts';
import HelloWorld from './HelloWorld';
import ClickMe from './ClickMe';
/**
* Get the click count from Oracle.
* @param {Object} that Component instance.
*/
const getCountOracle = (that) => {
Meteor.call('getCount', (error, records) => {
if (error) {
throw new Error('getCount failed');
}
console.log('getCount', records[0]);
that.setState({ oracleRecord: records[0] });
});
};
/**
* Increment the click count in Oracle.
* @param {Object} that Component instance.
*/
const incrementCountOracle = (that) => {
Meteor.call('incrementCount', (error, output) => {
if (error) {
throw new Error('incrementCount failed');
}
getCountOracle(that);
console.log('incrementCount', output);
});
};
/**
* Top level component.
* @return {Object} JSX transpiled code.
*/
class App extends React.Component {
/**
* Component constructor
*/
constructor() {
super();
this.state = {
oracleRecord: null,
};
this.handleOnClick = this.handleOnClick.bind(this);
}
/**
* React lifecycle hook for getting the initial click count from Oracle.
*/
componentDidMount() {
console.log('did mount');
getCountOracle(this);
}
/**
* Event handler for button onClick events.
*/
handleOnClick(event) {
event.preventDefault();
if (event.target.classList.contains('mongo')) {
Meteor.call('counts.increment', MongoCounts.findOne()._id);
} else {
incrementCountOracle(this);
}
}
/**
* React component render method.
* @return {Object} Transpiled JSX object.
*/
render() {
if (!this.props.mongoReady) {
return <div>Loading Mongo ...</div>;
}
if (!this.state.oracleRecord) {
return <div>Loading Oracle ...</div>;
}
return (
<div className="app">
<HelloWorld />
<ClickMe
db="mongo"
count={this.props.mongoRecord.count}
handleOnClick={this.handleOnClick}
/>
<ClickMe
db="oracle"
count={this.state.oracleRecord.COUNT}
handleOnClick={this.handleOnClick}
/>
</div>
);
}
}
App.propTypes = {
mongoReady: PropTypes.bool,
mongoRecord: PropTypes.instanceOf(Object),
count: PropTypes.number,
};
const tracker = () => {
const props = {};
let mongoHandle;
if (Meteor.isClient) {
mongoHandle = Meteor.subscribe('counts');
}
if (Meteor.isServer || (mongoHandle && mongoHandle.ready())) {
if (mongoHandle) {
props.mongoReady = mongoHandle.ready();
} else {
props.mongoReady = false;
}
props.mongoRecord = MongoCounts.findOne();
}
return props;
};
export default withTracker(tracker)(App);
And the following test code:
/* global describe it beforeEach */
import React from 'react';
import { assert } from 'chai';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import App from './App';
import HelloWorld from './HelloWorld';
import ClickMe from './ClickMe';
configure({ adapter: new Adapter() });
describe('App component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<App />);
});
it('should render without blowing up', () => {
assert.equal(1, wrapper.length);
});
it('should have one HelloWorld component', () => {
assert.equal(1, wrapper.find(HelloWorld).length);
});
it('should have two ClickMe components', () => {
assert.equal(2, wrapper.find(ClickMe).length);
});
});
Which fails with the following output:
I20181119-15:08:56.325(1)? --------------------------------
I20181119-15:08:56.325(1)? ----- RUNNING SERVER TESTS -----
I20181119-15:08:56.325(1)? --------------------------------
I20181119-15:08:56.325(1)?
I20181119-15:08:56.325(1)?
I20181119-15:08:56.325(1)?
I20181119-15:08:56.325(1)? App component
=> Meteor server restarted
I20181119-15:08:56.326(1)? ✓ should render without blowing up
I20181119-15:08:56.326(1)? 1) should have one HelloWorld component
I20181119-15:08:56.326(1)? 2) should have two ClickMe components
I20181119-15:08:56.326(1)?
I20181119-15:08:56.326(1)? ClickMe component
I20181119-15:08:56.326(1)? ✓ should render without blowing up
I20181119-15:08:56.327(1)?
I20181119-15:08:56.327(1)? HelloWorld component
I20181119-15:08:56.327(1)? ✓ should render without blowing up
I20181119-15:08:56.327(1)?
I20181119-15:08:56.327(1)?
I20181119-15:08:56.327(1)? 3 passing (46ms)
I20181119-15:08:56.327(1)? 2 failing
I20181119-15:08:56.327(1)?
I20181119-15:08:56.327(1)? 1) App component
I20181119-15:08:56.327(1)? should have one HelloWorld component:
I20181119-15:08:56.327(1)?
I20181119-15:08:56.328(1)? AssertionError: expected 1 to equal 0
I20181119-15:08:56.328(1)? + expected - actual
I20181119-15:08:56.328(1)?
I20181119-15:08:56.328(1)? -1
I20181119-15:08:56.328(1)? +0
I20181119-15:08:56.328(1)?
I20181119-15:08:56.328(1)? at Test.it (imports/ui/components/App.test.jsx:25:12)
I20181119-15:08:56.328(1)? at run (packages/meteortesting:mocha-core/server.js:34:29)
I20181119-15:08:56.328(1)? at Context.wrappedFunction (packages/meteortesting:mocha-core/server.js:63:33)
I20181119-15:08:56.328(1)? at run (packages/meteortesting:mocha-core/server.js:52:15)
I20181119-15:08:56.328(1)?
I20181119-15:08:56.329(1)? 2) App component
I20181119-15:08:56.329(1)? should have two ClickMe components:
I20181119-15:08:56.329(1)?
I20181119-15:08:56.329(1)? AssertionError: expected 2 to equal 0
I20181119-15:08:56.329(1)? + expected - actual
I20181119-15:08:56.329(1)?
I20181119-15:08:56.329(1)? -2
I20181119-15:08:56.329(1)? +0
I20181119-15:08:56.329(1)?
I20181119-15:08:56.329(1)? at Test.it (imports/ui/components/App.test.jsx:29:12)
I20181119-15:08:56.330(1)? at run (packages/meteortesting:mocha-core/server.js:34:29)
I20181119-15:08:56.330(1)? at Context.wrappedFunction (packages/meteortesting:mocha-core/server.js:63:33)
I20181119-15:08:56.330(1)? at run (packages/meteortesting:mocha-core/server.js:52:15)
I am pretty sure the issue stems from the with_tracker wrapper somehow (removing the wrapper make the test pass, but breaks other things)?