Open hyochan opened 4 years ago
I'm not sure if this issue is specific to mocking or unit testing in general but I had a really hard time getting unit tests around these chart components. For anyone who may end up here in the future, here's what I had to do to make my chart components testable. Using the following steps allowed me to reach 100% code coverage under Jest.
react-native-svg-charts
component onLayout
handlersreact-native-svg-charts
components listen for onLayout
changes before they render any data. You will need to trigger these event handlers for each component in your tests. This is a utility function I wrote to make that easy with @testing-library/react-native
.
function _findByProp(root: any, prop = '', found: any[] = []) {
if (!root) return found
if (root.props) {
if (Object.keys(root.props).includes(prop)) found.push(root)
}
if (root.children && root.children.length) {
root.children.forEach((c) => _findByProp(c, prop, found))
}
return found
}
/**
* Find components in the given component hierarchy based
* on the name of one of their props. For example,
* `findByProp(root, 'foo')` will return a list of all
* components with a `foo` prop of any value.
*/
export function findByProp(
/**
* The root of the component tree to search through.
*/
root: any,
/**
* The name of the prop you are using to select components.
*/
prop = '',
) {
return _findByProp(root, prop)
}
/**
* Fire the same layout event for all components that have
* an `onLayout` prop. Generally you won't want to do this because
* the layout dimensions would be different for each component.
* However, this can be useful if you don't have a way to determine
* which components should receive specific dimensions.
*/
export function fireLayoutEvent(
/**
* The root node to search for components with `onLayout` props.
*/
root: any,
/**
* The event options inside of `event.nativeElement.layout`
*/
options = {
width: 0,
height: 0,
},
) {
findByProp(root, 'onLayout').forEach((n) =>
fireEvent(n, 'layout', { nativeEvent: { layout: options } }),
)
}
Then in my test files:
beforeEach(() => {
const screen = render(component, options)
// Force a layout event.
fireLayoutEvent(screen.container, {width: 100, height: 100})
})
react-native-svg
Add a mock implementation of react-native-svg
from here: https://gist.github.com/JCMais/8302a1646ccc9759237947a66bdda8e0
Adding these mocks makes it really simple to query the components, especially if you use @testing-library/react-native
In order to query the chart elements, I was able to add accessibility attributes to the various chart components using the svg
prop they all have.
In my component code:
<XAxis
data={chartData}
svg={{
accessibilityRole: 'columnheader',
}}
></XAxis>
Then in my tests I was able to do the following:
// Get all x axis labels using the column header role
screen.getAllByRole('columnheader')
// Get an x axis label using its text
screen.getByText('January')
I was able to follow the accessibility paradigm described in the following article: https://tink.uk/accessible-svg-line-graphs/. This approach only works on web and test. For native I just treat my component as an image with a very descriptive label:
<Container
// On native, describe the chart as an image.
accessible={Platform.OS === 'web' ? undefined : true}
accessibilityLabel={Platform.OS === 'web' ? undefined : makeA11ySummary()}
accessibilityRole={Platform.OS === 'web' ? undefined : 'image'}
>
...
</Container>
I would love to know if there are better/easier approaches out there.
Any idea how we can mock this module and make better testing coverage?