How to use/configure Meteor 1.3 testing with Mocha and Enzyme?

Ok, I’ve been putting off unit testing for way too long, it’s getting absurd. :smiley: I’m using Meteor 1.3 beta 12, and I know it now supports testing via Mocha. Could someone clue me in on how to set up my Meteor project to use Enzyme? Pretend I’ve never used unit testing before.

6 Likes

Ok @tmeasday, here’s our official thread on integrating Enzyme into Meteor 1.3 testing! :slight_smile:

So, I’ve gotten it to work purely via npm. As we discussed previously, there’s some setup involved (below) that may not be necessary if used with Meteor (since Babel/ES6 is already integrated).

A .babelrc file must be added at the root:

{
  presets: ["airbnb"]
}

babel-register must be installed, and when running mocha, it must first read this setup file: (in /tests/.setup.js)

require('babel-register')();

var jsdom = require('jsdom').jsdom;

var exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

global.navigator = {
  userAgent: 'node.js'
};

documentRef = document;

After that, it’s all cake. Enzyme is just an extra set of testing functions and nothing more:

import React from 'react';
import { mount, shallow } from 'enzyme';

describe('<Foo />', () => {

  it('calls componentDidMount', () => {
    const wrapper = mount(<Foo />);
    expect(Foo.prototype.componentDidMount.calledOnce).to.equal(true);
  });

});

I don’t mind having to run the tests via npm, but of course it would be nicer if it were integrated into Meteor and had the cool browser display.

You’ve done better than me, I can’t get Meteor to recognise mount from enzyme. I’m on beta.12, hoping that rc8 might fix it when it gets fixed for Windows.

Really? The version of Meteor shouldn’t matter, since I’m just using npm and mocha to test. I’ll dump out the steps needed, and if you follow them it should be working for you:

EDIT: See the /.babelrc and /tests/.setup.js file above. Other than that:

Get into your project root and make a directory called tests. Run npm i --save-dev react-addons-test-utils enzyme jsdom mocha chai babel babel-register babel-preset-airbnb.

Make sure this is in your package.json:

  "scripts": {
    "test": "mocha tests/.setup.js tests/**/*-test.js"
  },

And you’re done! I tried the following component + test and it worked:

Thing.js

import React, { Component } from 'react';

export default class Thing extends Component {
  render() {
    return <div className="abc">Hi</div>
  }
}

Thing-test.js

import React from 'react';
import { expect } from 'chai';
import { shallow, mount, render } from 'enzyme';
import Thing from '../client/components/Thing';

describe('<Thing />', function () {
  it('contains a div with class abc and a hello message', function () {
    expect(shallow(<Thing />).contains(<div className="abc">Hi</div>)).to.equal(true);
  });
});

Though I wasn’t able to successfully test a stateless function. I might be missing something there.

@ffxsam that’s really kind of you to list all that out, I’ll give it a go shortly.

I was trying to follow the approach in the 1.3 guide which uses practicalmeteor:mocha (well it does in the text, the react-test branch of the todos app actually uses avital:mocha, although I think they may be the same thing). That’s why I was leaving it for the time being, I think Tom may still be working on all the testing stuff.

I had the 1.3 approach working fine for unit testing plain javascript but for some reason I couldn’t get my code to recognise { mount }.

I say ‘had’ it working, because as of some recent release I now seem to be getting the test runner results showing at the bottom of my app as well as on port 3100…

Oops, I almost forgot what thread I was in and duplicated content. :slight_smile: I’ll go back and trim it up.

Yeah, so that’s what this thread is about: getting Enzyme to play with practicalmeteor:mocha and Meteor 1.3, so we can get test results in the browser. (and to have the Meteor-specific tests work alongside that - testing methods, collections, etc)

Ah OK, perfect, I’m not going mad then! Let me know if there’s anything useful we can contribute.

Edit: @ffxsam is this thread the continuation of a github discussion? If so, could you post the link so I can get up to speed on the current status?

Nope, it’s a continuation of a private convo between Tom & me.

1 Like

FWIW, I think it’s better to test React components without Meteor at all and without practicalmeteor:mocha, and just use plain old mocha like @ffxsam has done.

  1. The isolated unit test will force your code to be less coupled to Meteor (good thing), and they will run much faster (good thing).

  2. The Meteor test mode is useful for testing the configuration of Meteor specific features, like publications etc. This is where unit tests are mostly useless.

  3. If you want to make sure that the combination of the react components and the Meteor features work well together, you can just write 1 or 2 end-to-end tests.

The in-app reporter will only work for point 2. In Velocity we tried had a single reporter for all 3 types, but this is no longer possible, so I wouldn’t try to force it. The real issue here is that we need a generic in-app reporter that can take results from the 1 and 3.

As an aside, notice that points 1, 2, and 3 relate directly to the testing triangle:

5 Likes

Great, problem solved then! :laughing: I’ll probably modify my npm test script to do mocha --watch and just leave it running. And I can use the official Meteor guide recommendations for testing Meteor-specific stuff.

I just had a google around for any issues with testing functional stateless components and didn’t spot anything. I’ll give it a try too and see if I have any luck.

It looks like Redux should fit well into the approach above too: http://redux.js.org/docs/recipes/WritingTests.html

@sashko - here’s the thread we had going on Enzyme.

What’s the best way to separate Meteor from React components? In particular, I’ve a React component that has a nested container (the container depends on Meteor to get its data) and when I import the top level component for my mocha test, it loads the container that in turn try to load some Meteor things and fails.

Files are a bit like:

top level component:

import ItemContainer from '../containers/ItemContainer';

export default class TopLevelComponent extends Component {
  render() {
    return <div>
      ...
      <ItemContainer />
    <div>
  }
}

ItemContainer:

// this depends on 'meteor/mongo' that mocha can't resolve
import * from '../../lib/collections';

import { composeWithTracker } from 'react-komposer';
import SecondLevelComponent from '../components/SecondLevelComponent';

// returns a container that loads some data
// and passes it to SecondLevelComponent that is a pure React component

With react-komposer’s disable I can prevent the nested container from running on test, but there is still the import of Mongo related thing that fails.

Other that this nested dependance, my components have no relation to Meteor but I still can’t get them to run with mocha because of the import dependencies

Are you using hwillson:stub-collections to stub out collections, and sinon to stub functions and methods?

At the moment it’s not so much that I need to use the collections (since thanks to react-komposer’s disable the nested container won’t run) but it’s just that mocha is not able to load my file because it ends trying to import meteor/mongo due to the file dependencies that make sense for the app, but are useless for the test since I just want to test the top level component.

Mocha should have no problem importing Meteor packages, if you’re using practicalmeteor:mocha and using that as your driver package (meteor test --driver-package practicalmeteor:mocha). Using mocha via npm will not work with Meteor, as it chokes on the meteor packages, as you’re experiencing.

1 Like

Nice. I’ll keep that in mind. However that starts an entire Meteor app (which sounds good for integration testing) but I’d like something that runs fast for my unit tests, and exits when all is good so that can be part of a deployment script.
Is there a way to make mocha ignore some imports? I’ve been looking at webpack config for a similar problem with kadira:storybook , but I’m not sure I can use that with mocha

I tried implementing the solution described by @ffxsam
Here is the repo: https://github.com/andersr/meteor-mocha-enzyme-testing

However, while the Mocha test runs as expected, I get the following error when attempting to run the actual Meteor app:

      => Errors prevented startup:                  
        
        While processing files with ecmascript (for target web.browser):
        native: Unexpected token p
        at Object.parse (native)
        at BabelCompiler.BCp._inferFromBabelRc
        (packages/babel-compiler/babel-compiler.js:164:1)
        at BabelCompiler.BCp.inferExtraBabelOptions
        (packages/babel-compiler/babel-compiler.js:154:1)
        at BabelCompiler.BCp.processOneFileForTarget
        (packages/babel-compiler/babel-compiler.js:97:1)
        at BabelCompiler.<anonymous>
        (packages/babel-compiler/babel-compiler.js:21:1)
        at Array.forEach (native)
        at BabelCompiler.BCp.processFilesForTarget
        (packages/babel-compiler/babel-compiler.js:20:1)
        
        
        While processing files with ecmascript (for target os.osx.x86_64):
        native: Unexpected token p
        at Object.parse (native)
        at BabelCompiler.BCp._inferFromBabelRc
        (packages/babel-compiler/babel-compiler.js:164:1)
        at BabelCompiler.BCp.inferExtraBabelOptions
        (packages/babel-compiler/babel-compiler.js:154:1)
        at BabelCompiler.BCp.processOneFileForTarget
        (packages/babel-compiler/babel-compiler.js:97:1)
        at BabelCompiler.<anonymous>
        (packages/babel-compiler/babel-compiler.js:21:1)
        at Array.forEach (native)
        at BabelCompiler.BCp.processFilesForTarget
        (packages/babel-compiler/babel-compiler.js:20:1)

The issue seems to be caused by the presence of the .babelrc file. When that file is removed, Meteor runs as expected but Mocha tests do not.

I attempted this and this workaround from a related github issue, but neither worked.

Did I maybe do something wrong in my implementation?