Aller au contenu principal

Tests avec formatjs

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Prérequis des API Intl

React Intl utilise les API Intl intégrées à JavaScript. Vérifiez que votre environnement satisfait aux exigences listées dans Prérequis des API Intl

Mocha

Si vous utilisez Mocha comme exécuteur de tests et testez sur des environnements JavaScript anciens, vous pouvez charger le polyfill Intl via la CLI ou en ajoutant une balise <script> dans le navigateur.

Ligne de commande

Exécutez mocha avec polyfill automatique si nécessaire :

$ mocha --recursive test/

Vous pouvez soit charger le polyfill dans le navigateur depuis node_modules, soit utiliser le service polyfill-fastly.io du Financial Times :

<script src="https://polyfill-fastly.io/v2/polyfill.min.js?features=Intl,Intl.~locale.en-US"></script>

Rendu Superficiel (Shallow Rendering)

Le package react-addons-test-utils de React inclut une fonctionnalité de rendu superficiel que vous pouvez utiliser pour tester les composants React de votre application. Si un composant que vous testez avec ReactShallowRenderer utilise React Intl - notamment injectIntl() - vous devrez effectuer une configuration supplémentaire car les composants React Intl nécessitent d'être encapsulés dans un <IntlProvider>.

Tester des composants exemples utilisant React Intl

Les exemples suivants supposent l'utilisation du framework de test mocha, expect et expect-jsx.

ShortDate (Basique)

import React from 'react'
import {FormattedDate} from 'react-intl'

const ShortDate = props => (
<FormattedDate
value={props.date}
year="numeric"
month="short"
day="2-digit"
/>
)

export default ShortDate

Tester le composant exemple <ShortDate> ne diffère pas du test d'un autre composant basique de votre application utilisant le rendu superficiel de React :

import expect from 'expect'
import expectJSX from 'expect-jsx'
import React from 'react'
import {createRenderer} from 'react-addons-test-utils'
import {FormattedDate} from 'react-intl'
import ShortDate from '../short-date'

expect.extend(expectJSX)

describe('<ShortDate>', function () {
it('renders', function () {
const renderer = createRenderer()
const date = new Date()

renderer.render(<ShortDate date={date} />)
expect(renderer.getRenderOutput()).toEqualJSX(
<FormattedDate value={date} year="numeric" month="short" day="2-digit" />
)
})
})

Rendu DOM

Si vous utilisez le DOM dans vos tests, vous devez fournir le contexte IntlProvider à vos composants par composition :

let element = ReactTestUtils.renderIntoDocument(
<IntlProvider>
<MyComponent />
</IntlProvider>
)

Cependant, la référence element pointe désormais vers le IntlProvider plutôt que vers votre composant. Pour récupérer une référence à votre composant encapsulé, utilisez les "refs" avec ces modifications du code :

Dans votre composant, ajoutez {forwardRef: true} lors de l'appel à injectIntl() :

class MyComponent extends React.Component {
...
myClassFn() { ... }
}
export default injectIntl(MyComponent, {forwardRef: true});

Dans votre test, ajoutez une "ref" pour extraire la référence à votre composant testé :

const element = React.createRef()
ReactTestUtils.renderIntoDocument(
<IntlProvider>
<MyComponent ref={element} />
</IntlProvider>
)

Vous pouvez désormais accéder à l'instance du composant encapsulé via element comme suit :

element.current.myClassFn()

Fonction utilitaire

Puisque vous devrez répéter cette opération dans tous vos tests unitaires, encapsulez cette configuration dans une fonction render :

function renderWithIntl(element) {
let instance

ReactTestUtils.renderIntoDocument(
<IntlProvider>
{React.cloneElement(element, {
ref: instance,
})}
</IntlProvider>
)

return instance
}

Utilisez-la désormais dans vos tests ainsi :

const element = React.createRef();
renderWithIntl(<MyElement ref={element}>);
element.current.myClassFn();

Enzyme

Les tests avec Enzyme fonctionnent de manière similaire. Vos composants mount()és et shallow()és auront besoin d'accéder au contexte intl. Voici une fonction utilitaire importable pour monter vos composants utilisant les fonctionnalités de React-Intl (composants <Formatted* /> ou méthodes format*() via injectIntl).

Fonction utilitaire

/**
* Components using the react-intl module require access to the intl context.
* This is not available when mounting single components in Enzyme.
* These helper functions aim to address that and wrap a valid,
* English-locale intl context around them.
*/

import React from 'react'
import {IntlProvider} from 'react-intl'
import {mount, shallow} from 'enzyme'

// You can pass your messages to the IntlProvider. Optional: remove if unneeded.
const messages = require('../locales/en') // en.json
const defaultLocale = 'en'
const locale = defaultLocale

export function mountWithIntl(node: React.ReactElement) {
return mount(node, {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale,
defaultLocale,
messages,
},
})
}

export function shallowWithIntl(node: React.ReactElement) {
return shallow(node, {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale,
defaultLocale,
messages,
},
})
}

Utilisation

Créez un fichier contenant cet utilitaire (ex: helpers/intl-enzyme-test-helper.js) et import les méthodes nécessaires dans vos tests.

// intl-enzyme-test-helper.js

import {mountWithIntl} from 'helpers/intl-enzyme-test-helper.js'

const wrapper = mountWithIntl(<CustomComponent />)

expect(wrapper.state('foo')).to.equal('bar') // OK
expect(wrapper.text()).to.equal('Hello World!') // OK

Basé sur ce gist.

Jest

Les tests avec Jest se divisent en deux approches : les snapshots et les tests DOM. Les snapshots sont une fonctionnalité récente et fonctionnent nativement. Pour les tests DOM, utilisez Enzyme ou React TestUtils.

Tests par Snapshots

Les tests par instantanés (snapshot testing) sont une nouvelle fonctionnalité de Jest qui génère automatiquement des instantanés textuels de vos composants et les sauvegarde sur le disque. Ainsi, si le rendu de l'UI change, vous en êtes notifié sans avoir à écrire manuellement d'assertions sur le résultat du composant. Utilisez soit une fonction d'aide soit un mock comme décrit ci-dessous.

Fonction d'aide

import React from 'react'
import renderer from 'react-test-renderer'
import {IntlProvider} from 'react-intl'

const createComponentWithIntl = (children, props = {locale: 'en'}) => {
return renderer.create(<IntlProvider {...props}>{children}</IntlProvider>)
}

export default createComponentWithIntl

Utilisation

import React from 'react'
import createComponentWithIntl from '@site/utils/createComponentWithIntl'
import AppMain from '../AppMain'

test('app main should be rendered', () => {
const component = createComponentWithIntl(<AppMain />)

let tree = component.toJSON()

expect(tree).toMatchSnapshot()

tree.props.onClick()

tree = component.toJSON()

expect(tree).toMatchSnapshot()
})

Vous trouverez un exemple exécutable ici et plus d'informations sur Jest ici.

Utilisation avec Jest & enzyme

Jest mocker automatiquement react-intl, donc aucune implémentation supplémentaire n'est nécessaire. Les tests doivent fonctionner tels quels :

import React from 'react'
import {shallow} from 'enzyme'
import AppMain from '../AppMain'

test('app main should be rendered', () => {
const wrapper = shallow(<AppMain />)
expect(wrapper).toMatchSnapshot()
})

Tests DOM

Si vous souhaitez utiliser Jest avec des tests DOM, consultez les informations ci-dessus dans la section Enzyme ou la documentation officielle de Jest.

Storybook

Intl

Pour utiliser react-intl dans Storybook, vous pouvez employer storybook-addon-intl qui fournit un wrapper facile à utiliser pour react-intl, incluant un sélecteur de langue pour tester vos composants dans toutes les langues disponibles.

react-testing-library

Pour utiliser conjointement react-intl et react-testing-library, vous devez fournir une fonction d'aide dans le flux de test.

Consultez la documentation.

Pour créer une solution générique, nous pouvons concevoir une fonction render personnalisée en utilisant l'option wrapper comme expliqué dans la page de configuration.
Notre fonction render personnalisée peut ressembler à ceci :

// test-utils.js
import React from 'react'
import {render as rtlRender} from '@testing-library/react'
import {IntlProvider} from 'react-intl'

function render(ui, {locale = 'pt', ...renderOptions} = {}) {
function Wrapper({children}) {
return <IntlProvider locale={locale}>{children}</IntlProvider>
}
return rtlRender(ui, {wrapper: Wrapper, ...renderOptions})
}

// re-export everything
export * from '@testing-library/react'

// override render method
export {render}
import React from 'react'
import '@​testing-library/jest-dom/jest-globals'
// We're importing from our own created test-utils and not RTL's
import {render, screen} from '../test-utils.js'
import {FormattedDate} from 'react-intl'

const FormatDateView = () => {
return (
<div data-testid="date-display">
<FormattedDate
value="2019-03-11"
timeZone="utc"
day="2-digit"
month="2-digit"
year="numeric"
/>
</div>
)
}

test('it should render FormattedDate and have a formated pt date', () => {
render(<FormatDateView />)
expect(screen.getByTestId('date-display')).toHaveTextContent('11/03/2019')
})