Reputation: 69983
I am new to React, and I am testing my components. When implementing snapshot testing I have found these two approaches:
Approach 1
import React from 'react'
import renderer from 'react-test-renderer'
import Projects from './Projects'
it('renders Projects component properly', () => {
const tree = renderer.create(<Projects />)
expect(tree).toMatchSnapshot()
})
Approach 2
import React from 'react'
import renderer from 'react-test-renderer'
import Projects from './Projects'
it('renders Projects component properly', () => {
const component = renderer.create(<Projects />)
const tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
Is it important to call toJSON
in the component (approach 2) or can you just pass it directly to check toMatchSnapshot
(approach 1)? Can somebody explain me the difference between both approaches in terms of performance?
Upvotes: 3
Views: 3545
Reputation: 3230
TL;DR
When toMatchSnapshot
matcher is used toJSON
is called on the object passed to expect
. It is a part of some kind of chain of checks that may have a neglectable, for a vast majority of real cases, performance impact. I would suggest calling toJSON
just to follow "explicit is better than implicit" principle and the fact that the inner behavior is subject to change.
Explanation
The matcher uses SnapshotStates match
method that serializes object. serialize
just calls pretty-format with a set of plugins.
This is the entry point of pretty-format, I've added comments there:
function prettyFormat(
val: unknown,
options?: PrettyFormat.OptionsReceived,
): string {
if (options) {
validateOptions(options);
if (options.plugins) {
const plugin = findPlugin(options.plugins, val);
/* When val is a result of `toJSON` call
it has a $$typeof: Symbol.for('react.test.json') field so
it is handled by ReactTestComponent plugin in this point */
if (plugin !== null) {
return printPlugin(plugin, val, getConfig(options), '', 0, []);
}
}
}
// When `toJSON` is not called `printBasicValue` is invoked but is it pointless
const basicResult = printBasicValue(
val,
getPrintFunctionName(options),
getEscapeRegex(options),
getEscapeString(options),
);
if (basicResult !== null) {
return basicResult;
}
return printComplexValue(val, getConfig(options), '', 0, []);
}
printBasicValue
simply performs a set of checks, all of them fail for an object, so the printComplexValue
takes its turn.
Here is a fragment where .toJSON()
is called.
if (
config.callToJSON &&
!hitMaxDepth &&
val.toJSON &&
typeof val.toJSON === 'function' &&
!hasCalledToJSON
) {
return printer(val.toJSON(), config, indentation, depth, refs, true);
}
printer
finds a plugin and invokes it at the very beggining so it does not matter.
const plugin = findPlugin(config.plugins, val);
if (plugin !== null) {
return printPlugin(plugin, val, config, indentation, depth, refs);
}
You can notice that calling toJSON
may be disabled by setting a config option or by some other conditions that I believe do not matter for the case of snapshot, but still.
Upvotes: 5