Do you want to request a _feature_ or report a _bug_?
Neither: _documentation_
What is the current behavior?
Mocking a module that exports just a react component is well documented (and quite pleasant):
// ./Link.jsx
import Icon from './Icon'
const Link = ({children, href, icon}) => <a href={href}><Icon type={icon} />{children}</a>
export default Link
// ./__tests__/Link-test.jsx
import renderer from 'react-test-renderer'
import Link from '../Link'
jest.mock('../Icon', () => 'Icon')
describe('Link', () => {
it('should render correctly', () => {
const component = renderer.create(
<Link href="http://facebook.com/" icon="facebook">
Facebook
</Link>
)
expect(component.toJSON()).toMatchSnapshot()
})
})
What is the expected behavior?
That it's just as easy to mock components when you're using a library like redux-form (which exports multiple react components, and HOCs and more).
Something like this (examples simplified for brevity):
// ./Form.jsx
import { Field, reduxForm } from 'redux-form/immutable'
const Form = props => (
<form {...props}>
<Field component="input" name="title" />
<Field component="textarea" name="description" />
</form>
)
export default reduxForm({
form: 'foo',
})(Form)
// ./__tests__/Form-test.jsx
import renderer from 'react-test-renderer'
import Form from '../Form'
jest.mock('redux-form/immutable', () => ({
Field: () => 'Field',
reduxForm: () => (component) => component, // just pass through component without decorating it
}))
describe('Form', () => {
it('should render correctly', () => {
const component = renderer.create(
<Form />
)
expect(component.toJSON()).toMatchSnapshot()
})
})
Attempting to do the above will give the following error:
console.error node_modules/fbjs/lib/warning.js:36
Warning: Field(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
As a workaround I'm creating new modules that do nothing but export one component at a time and mock that, but there must be a better way:
// ./Field.jsx
export { Field as default } from 'redux-form/immutable'
// ./Form.jsx
import Field from './Field'
import { reduxForm } from 'redux-form/immutable'
const Form = props => (
<form {...props}>
<Field component="input" name="title" />
<Field component="textarea" name="description" />
</form>
)
export default reduxForm({
form: 'foo',
})(Form)
// ./__tests__/Form-test.jsx
import renderer from 'react-test-renderer'
import Form from '../Form'
jest.mock('./Field', () => 'Field')
describe('Form', () => {
it('should render correctly', () => {
const component = renderer.create(
<Form />
)
expect(component.toJSON()).toMatchSnapshot()
})
})
Run Jest again with --debug
and provide the full configuration it prints. Please mention your node and npm version and operating system.
jest: v15.0.2
osx: v10.11.6
node: v6.5.0
You can do require.requireActual
to get the real module and create some mocks for it.
jest.mock('Form', () => {
const RealModule = require.requireActual('Form');
const MyModule = {
RealThing: RealModule.RealThing,
鈥dd some mocks
};
return MyModule;
});
is that what you are looking for?
thanks @cpojer that's half of it.
The other half was a misconception.
In my example I did this:
Field: () => 'Field',
But I should've done this: Field: 'Field',
I can submit a PR with snapshots + mocks examples here: http://facebook.github.io/jest/docs/tutorial-react.html#content
If you guys thinks it makes sense.
I think it will help others like me who's migrating their testing suites to jest in order to use snapshot testing and the awesome mocking features.
Hey! Yeah I'm totally happy to accept a PR that explains how to create mocks (and why) for certain components when using snapshot testing :)
Thank you @cpojer and @stipsan. @stipsan's brief guide above was extremely helpful for me.
jest.mock('redux-form/immutable', () => ({
Field: 'Field',
reduxForm: (redux_form_configuration) => (wrapped_form_component) => wrapped_form_component
}))
i've tested above code and it works. I reckon many people would find this exceptionally useful.
Thanks for raising this issue!!
we are not testing redux-form here, so I think its right to mock both field
and redux-form
method
If someone is looking for a solution to this issue, here's how I do it:
import * as moduleToMock from 'somemodule';
moduleToMock.propToMock = (props) => {/* do whatever you need here */ } ;
jest.setMock('somemodule', moduleToMock);
require.requireActual only gets the default export when I tried it.
@SpadarShut your solution is perfect! Thank you for sharing!
in addition to @SpadarShut answer import could be done like this
const moduleToMock = require('somemodule');
in my case it's useful to avoid TS errors
If the object you are trying to mock only has a getter
, @SpadarShut's solution won't work, I would get an error:
// the following line triggers an error
moduleToMock.propToMock = (props) => {/* do whatever you need here */ } ;
>TypeError: Cannot set property RefinementList of [object Object] which has only a getter
The only solution that works is @cpojer's answer which I rewrote here slightly differently because I only wanted to mock one _named export_ of my module and keep the other exports un-mocked.
jest.mock("SomeModule", () => {
const RealModule = require.requireActual("somemodule");
const MyModule = {
...RealModule,
SomeComponentToMock: jest.fn(() => null)
};
return MyModule;
});
is there somewhere a complete working example?
I want to mock a DropDown or a Modal component from @fluentui/react.
I do not understand, how to import the module and just replace only the DropDown component, which is a component of properties and state.
What I try is something like this:
let module = require.requireActual("@fluentUi/react");
module.Modal = () => <>test</>;
jest.mock("@fluentUi/react", () => {
return module;
});
when I now mount my component, which renders the modal as one of its childs, I would expect that <>test> would get returned instead of the origin modal component. But actually the mock does not have any impacts on my tests.
Most helpful comment
If someone is looking for a solution to this issue, here's how I do it: