J'ai un composant qui est enveloppé à la fois dans un HOC Material-UI withStyles
et un HOC React memo
.
Je ne peux pas tester ce composant car je ne peux pas appeler dive()
:
ShallowWrapper :: dive () ne peut être appelé que sur les composants
La seule option dont je suis actuellement au courant est de exporter la démo
indépendamment et exporter par défaut avec les styles (styles) (démo)
. Cela me permet de tester le composant qui n'est pas enveloppé dans withStyles
. Je voudrais éviter cette méthode.
Si je supprime memo (), je peux tester le composant. De même, si je supprime withStyles (), je suis également en mesure de tester le composant. La combinaison de ces HOC rend mon composant non testable.
Quelles sont les stratégies disponibles pour tester efficacement ce composant?
demo.js p >
import React from "react"; import Adapter from "enzyme-adapter-react-16"; import { configure, shallow } from "enzyme"; import Demo from "./demo"; import MUIIconButton from "@material-ui/core/IconButton"; import Tooltip from "@material-ui/core/Tooltip"; configure({ adapter: new Adapter() }); describe("Demo", () => { it("Should have a tooltip with label", () => { const tooltip = "My tooltip"; const el = shallow(<Demo label={tooltip} />).dive(); expect(el.find(Tooltip).props().title).toEqual(tooltip); }); });
demo.test.js
import React, { memo } from "react"; import MUIIconButton from "@material-ui/core/IconButton"; import { withStyles } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; import Typography from "@material-ui/core/Typography"; const styles = () => ({ root: { backgroundColor: "red" /* more styles... */ } }); const Demo = memo(({ label, classes }) => ( <div className={classes.root}> <Tooltip disableFocusListener title={label}> <Typography>label</Typography> </Tooltip> </div> )); export default withStyles(styles)(Demo);
Sandbox pleinement opérationnel
3 Réponses :
Comme le suggère skyboyer, vous devez simplement exporter
la fonction mémorisée. Vous pouvez importer
l'exportation par défaut HOC
et utiliser mount
, mais vous devrez simuler l'objet classes
pour correspond à la façon dont il est utilisé dans le composant.
Exemple de travail : https: // codesandbox.io/s/4r492qvoz9
components/Demo/demo.js
import React from "react"; import Adapter from "enzyme-adapter-react-16"; import { configure, shallow, mount } from "enzyme"; import { Demo } from "../demo"; import HOCDemo from "../demo"; configure({ adapter: new Adapter() }); const initialProps = { label: "My tooltip", classes: { root: "component-example" } }; const shallowWrapper = shallow(<Demo {...initialProps} />); const mountWrapper = mount(<Demo {...initialProps} />); const mountHOComponent = mount(<HOCDemo {...initialProps} />); describe("Demo", () => { afterAll(() => { shallowWrapper.unmount(); mountWrapper.unmount(); }); it("shallowWrap renders a tooltip with label", () => { expect(shallowWrapper.find("WithStyles(Tooltip)").props().title).toBe( initialProps.label ); }); it("mountWrap renders a tooltip with label", () => { expect(mountWrapper.find("Tooltip").props().title).toBe(initialProps.label); }); it("mountHOComponent renders a tooltip with label", () => { expect(mountHOComponent.find("Tooltip").props().title).toBe( initialProps.label ); }); });
components / Demo / __ tests __ / demo.test.js si jamais besoin de voir la structure DOM
, alors utilisez simplement console.log (wrapper.debug ()); code> - par exemple
console.log(mountHOComponent.debug());
)
import React, { memo } from "react"; import MUIIconButton from "@material-ui/core/IconButton"; import { withStyles } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; import Typography from "@material-ui/core/Typography"; const styles = () => ({ root: { backgroundColor: "red" /* more styles... */ } }); export const Demo = memo(({ label, classes }) => { return ( <div className={classes.root}> <Tooltip disableFocusListener title={label}> <Typography>label</Typography> </Tooltip> </div> ); }); export default withStyles(styles)(Demo);
Merci, nous avons décidé de mordre la balle et de suivre cette approche et cela fonctionne bien. Mais je ne sais toujours pas vraiment pourquoi une plongée peu profonde (el) .dive (). Dive () (Une plongée par règle HOC) n'a pas fonctionné comme prévu.
Ce problème est désormais résolu à partir de enzyme-adapter-react-16 v1.13.0 a >, qui a ajouté le support de memo dive (). Voici un bac à sable fourchu avec la dépendance mise à jour pour montrer que les deux méthodes de test (solution de contournement de plongée et d'exportation) réussissent maintenant.
Quand j'enroule avec un mémo, j'obtiens une forme qui ressemble à ceci
wrapper = mount(Layout) wrapper.find('Memo(Foo)')
donc dans mon test de plaisanterie, je peux obtenir le composant interne en référençant la clé de type
XXX
C'est parfait pour les tests unitaires superficiels.
Si je montais un composant parent et que je recherchais des enfants, vous pourriez faire quelque chose comme ceci:
import MemoizedFoo from './Foo' const Foo = MemoizedFoo.type describe() { it() { shallow(Foo) ...etc } }
consultez github.com/mui-org/material-ui/issues/9266 a> - il existe une solution de contournement avec
createShallow
. Mais je préfère continuer à utiliser l'exportation séparée comme vous le faites déjà. C'est un moyen plus fiable. Dans certains cas, comme les composants connectés à Redux, c'est comme une suggestion officielle. Ne serait-il donc pas préférable d'utiliser la même approche sur différents composants?