Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Make React PropType warnings throw errors with mocha.js, enzyme.js and sinon.js

Make React PropType warnings throw errors with enzyme.js + sinon.js + mocha.js

A simple stateless functional component that we want to test that it renders without propType warnings.

import React, { PropTypes } from 'react'

let VersionListItem = function ({ active, version }) {
  return (
    <a href='#' className={`list-group-item ${active}`}>
      <strong
        className='list-group-item-heading'>
        Version {version}
      </strong>
    </a>
  )
}
VersionListItem.propTypes = {
  active: PropTypes.string,
  version: PropTypes.number
}
export default VersionListItem
import { describe, it, before, after } from 'mocha'
import sinon from 'sinon'
import React from 'react'
import { shallow } from 'enzyme'
import VersionListItem from './version-list-item'

// Since react will console.error propType warnings, that which we'd rather have
// as errors, we use sinon.js to stub it into throwing these warning as errors
// instead.
before(() => {
  sinon.stub(console, 'error', (warning) => { throw new Error(warning) })
})
// While not forgetting to restore it afterwards
after(() => { console.error.restore() })

describe('<VersionListItem />', () => {
  const props = {
    active: 'active',
    version: 1
  }

  it('renders without error', () => {
    shallow(<VersionListItem {...props} />)
    // In this case there is no need for an assertion since we're only
    // interested in not getting any errors. And mocha will mark the test as a
    // failure if an error is thrown. :)
  })
})

Related reading:

@ascoppa

This comment has been minimized.

Show comment
Hide comment
@ascoppa

ascoppa Aug 20, 2016

If you're only interested on testing PropTypes shouldn't you omit calling shallow and just write <VersionListItem {...props} /> ?

Works for me and keeps the test case simple (with just one concern).

ascoppa commented Aug 20, 2016

If you're only interested on testing PropTypes shouldn't you omit calling shallow and just write <VersionListItem {...props} /> ?

Works for me and keeps the test case simple (with just one concern).

@AleksOrl

This comment has been minimized.

Show comment
Hide comment
@AleksOrl

AleksOrl Dec 1, 2016

I use this arcticle as an example for testing components in my project. I have found strange behaviour. I wrote about it on StackOverflow. How to avoid this problem?

AleksOrl commented Dec 1, 2016

I use this arcticle as an example for testing components in my project. I have found strange behaviour. I wrote about it on StackOverflow. How to avoid this problem?

@NaveenThally

This comment has been minimized.

Show comment
Hide comment
@NaveenThally

NaveenThally Jan 13, 2017

How do i unit test the warnings returned by PropType , i wanted to unit test i.e, on setting a specific value the PropType produces a warning which i wanted to Unit test for the code coverage.

NaveenThally commented Jan 13, 2017

How do i unit test the warnings returned by PropType , i wanted to unit test i.e, on setting a specific value the PropType produces a warning which i wanted to Unit test for the code coverage.

@scmx

This comment has been minimized.

Show comment
Hide comment
@scmx

scmx Feb 3, 2017

Wow, I had no idea there were comments, forks and lots of stars here. Glad you all found it useful. I barely remember writing this.
@ascoppa good point.
@NaveenThally: Take a look at the sinon documentation http://sinonjs.org/ You might want to use a spy instead. Related reading https://www.sitepoint.com/sinon-tutorial-javascript-testing-mocks-spies-stubs/

Owner

scmx commented Feb 3, 2017

Wow, I had no idea there were comments, forks and lots of stars here. Glad you all found it useful. I barely remember writing this.
@ascoppa good point.
@NaveenThally: Take a look at the sinon documentation http://sinonjs.org/ You might want to use a spy instead. Related reading https://www.sitepoint.com/sinon-tutorial-javascript-testing-mocks-spies-stubs/

@hulkish

This comment has been minimized.

Show comment
Hide comment
@hulkish

hulkish Mar 5, 2017

I found this to be a bit less polluting:

in my helpers.unit.js:

import sinon from 'sinon';
sinon.stub(console, 'error', (warning) => {
  if (warning && warning.indexOf('Warning: Failed prop type:') > -1) {
    process.nextTick(() => {
      throw new Error(warning);
    });
  }
});

then i run: mocha --require helper.unit.js --compilers babel-register mytest.test.js

hulkish commented Mar 5, 2017

I found this to be a bit less polluting:

in my helpers.unit.js:

import sinon from 'sinon';
sinon.stub(console, 'error', (warning) => {
  if (warning && warning.indexOf('Warning: Failed prop type:') > -1) {
    process.nextTick(() => {
      throw new Error(warning);
    });
  }
});

then i run: mocha --require helper.unit.js --compilers babel-register mytest.test.js

@scmx

This comment has been minimized.

Show comment
Hide comment
@scmx

scmx Mar 8, 2017

@hulkish: Cool.
Protip, see https://mochajs.org/#mochaopts (if you're not already aware of it)

Mocha will attempt to load ./test/mocha.opts as a configuration file of sorts. The lines in this file are combined with any command-line arguments.

Owner

scmx commented Mar 8, 2017

@hulkish: Cool.
Protip, see https://mochajs.org/#mochaopts (if you're not already aware of it)

Mocha will attempt to load ./test/mocha.opts as a configuration file of sorts. The lines in this file are combined with any command-line arguments.

@janzenz

This comment has been minimized.

Show comment
Hide comment
@janzenz

janzenz Mar 9, 2017

@scmx is the console.error.restore() part really necessary? I tried omitting it and there were no issues. I'm not also sure what that part actually does, I tried using it on my Chrome's console and it doesn't recognize restore() as a function. I also looked for it in the documentation but couldn't find any. Am I missing something? Thank you for sharing this!

janzenz commented Mar 9, 2017

@scmx is the console.error.restore() part really necessary? I tried omitting it and there were no issues. I'm not also sure what that part actually does, I tried using it on my Chrome's console and it doesn't recognize restore() as a function. I also looked for it in the documentation but couldn't find any. Am I missing something? Thank you for sharing this!

@evandavis

This comment has been minimized.

Show comment
Hide comment
@evandavis

evandavis Mar 9, 2017

In my mocha env, this only throws on first run. If I am watching the tests, subsequent runs pass (because prop warnings don't fire.) Has anyone else experienced that?

evandavis commented Mar 9, 2017

In my mocha env, this only throws on first run. If I am watching the tests, subsequent runs pass (because prop warnings don't fire.) Has anyone else experienced that?

@scmx

This comment has been minimized.

Show comment
Hide comment
@scmx

scmx Mar 28, 2017

@janzenz: Sinon stubs console.error to be different and provides the restore function to restore console.error to the original it was before. See http://sinonjs.org/releases/v2.1.0/stubs/

@evandavis: Try changing to beforeEach and afterEach instead of before and after. Perhaps it's being restored too soon.

Owner

scmx commented Mar 28, 2017

@janzenz: Sinon stubs console.error to be different and provides the restore function to restore console.error to the original it was before. See http://sinonjs.org/releases/v2.1.0/stubs/

@evandavis: Try changing to beforeEach and afterEach instead of before and after. Perhaps it's being restored too soon.

@jgkingston

This comment has been minimized.

Show comment
Hide comment
@jgkingston

jgkingston Dec 6, 2017

FWIW (and a bite late to the conversation), but I found this to be helpful in our test setup/helper file

import sinon from 'sinon';

const propTypeWarningMatch = sinon.match((warning) => {
  return typeof(warning) === 'string' &&
    warning.indexOf('Warning: Failed prop type:') > -1;
});

sinon.stub(console, 'error');

console.error.withArgs(propTypeWarningMatch).callsFake((warning) => {
  throw new ReferenceError(warning);
});

console.error.callThrough();

The .callThrough() is necessary to ensure console.error()'s default behavior is used for all warnings that don't match the custom match function.

jgkingston commented Dec 6, 2017

FWIW (and a bite late to the conversation), but I found this to be helpful in our test setup/helper file

import sinon from 'sinon';

const propTypeWarningMatch = sinon.match((warning) => {
  return typeof(warning) === 'string' &&
    warning.indexOf('Warning: Failed prop type:') > -1;
});

sinon.stub(console, 'error');

console.error.withArgs(propTypeWarningMatch).callsFake((warning) => {
  throw new ReferenceError(warning);
});

console.error.callThrough();

The .callThrough() is necessary to ensure console.error()'s default behavior is used for all warnings that don't match the custom match function.

@fatso83

This comment has been minimized.

Show comment
Hide comment
@fatso83

fatso83 Mar 14, 2018

@hulkish: that snippet doesn't actually fail any of my tests. Removing the process.nextTick fixes it.

Also, we removed the third parameter to #stub in Sinon 2 in favor of #callThrough(). @jgkingston's snippet is the way to go nowadays.

fatso83 commented Mar 14, 2018

@hulkish: that snippet doesn't actually fail any of my tests. Removing the process.nextTick fixes it.

Also, we removed the third parameter to #stub in Sinon 2 in favor of #callThrough(). @jgkingston's snippet is the way to go nowadays.

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