Vai al contenuto principale

Testing con formatjs

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

Requisiti delle API Intl

React Intl utilizza le API Intl integrate in JavaScript. Assicurati che il tuo ambiente soddisfi i requisiti elencati nella guida Requisiti delle API Intl

Mocha

Se utilizzi Mocha come test runner e esegui test su runtime JavaScript obsoleti, puoi caricare l'Intl Polyfill tramite CLI o aggiungendo uno <script> nel browser.

Riga di comando

Esegui mocha applicando automaticamente il polyfill se necessario:

$ mocha --recursive test/

Browser

Puoi caricare il polyfill nel browser da node_modules oppure utilizzare il servizio polyfill-fastly.io di Financial Times:

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

Shallow Rendering

Il pacchetto react-addons-test-utils di React include la funzionalità di shallow rendering che potresti utilizzare per testare i componenti React della tua applicazione. Se un componente che stai testando con ReactShallowRenderer utilizza React Intl — in particolare injectIntl() — dovrai eseguire configurazioni aggiuntive poiché i componenti React Intl richiedono di essere annidati all'interno di un <IntlProvider>.

Test di componenti d'esempio che utilizzano React Intl

I seguenti esempi presuppongono l'utilizzo di mocha, expect e expect-jsx come framework di test.

ShortDate (Base)

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

Testare il componente d'esempio <ShortDate> non differisce dal testare qualsiasi altro componente base nella tua app utilizzando lo shallow rendering di 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" />
)
})
})

DOM Rendering

Se utilizzi il DOM nei tuoi test, devi fornire il contesto IntlProvider ai tuoi componenti mediante composizione:

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

Tuttavia ciò significa che il riferimento element punta ora all'IntlProvider invece che al tuo componente. Per ottenere un riferimento al componente wrappato, puoi utilizzare i "ref" apportando queste modifiche al codice:

Nel tuo componente, ricorda di aggiungere {forwardRef: true} quando chiami injectIntl():

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

Nel tuo test, aggiungi un "ref" per estrarre il riferimento al componente testato:

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

Ora puoi accedere all'istanza del componente wrappato da element in questo modo:

element.current.myClassFn()

Funzione helper

Dovendo ripetere questa operazione in tutti i test unitari, conviene incapsulare la configurazione in una funzione render come questa:

function renderWithIntl(element) {
let instance

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

return instance
}

Ora puoi utilizzarla nei tuoi test in questo modo:

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

Enzyme

Il testing con Enzyme funziona in modo simile a quanto descritto sopra. I componenti mount()ati e shallow()ati necessiteranno dell'accesso al contesto intl. Di seguito una funzione helper importabile per montare componenti che utilizzano qualsiasi elemento di React-Intl (sia componenti <Formatted* /> che metodi format*() tramite injectIntl).

Funzione helper

/**
* 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,
},
})
}

Utilizzo

Crea un file contenente l'helper sopra definito, ad esempio helpers/intl-enzyme-test-helper.js, e import i metodi necessari nei tuoi test.

// 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

Basato su questo gist.

Jest

Il testing con Jest può essere suddiviso in due approcci: snapshot testing e DOM testing. Lo snapshot testing è una funzionalità relativamente recente e funziona out-of-the-box. Per il DOM testing è necessario utilizzare Enzyme o React TestUtils.

Snapshot Testing

Il testing degli snapshot è una nuova funzionalità di Jest che genera automaticamente snapshot testuali dei tuoi componenti e li salva su disco, così se l'output dell'interfaccia utente cambia, ne vieni informato senza dover scrivere manualmente alcuna asserzione sull'output del componente. Utilizza una funzione helper o un mock come descritto di seguito.

Funzione helper

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

Utilizzo

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()
})

Puoi trovare un esempio eseguibile qui e maggiori informazioni su Jest qui.

Utilizzo con Jest & enzyme

Jest simulerà automaticamente react-intl, quindi non è necessaria alcuna implementazione aggiuntiva, i test dovrebbero funzionare così come sono:

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()
})

Testing del DOM

Se desideri utilizzare Jest con il testing del DOM, leggi maggiori informazioni nella sezione Enzyme o nella documentazione ufficiale di Jest.

Storybook

Intl

Se vuoi utilizzare react-intl all'interno di Storybook puoi usare storybook-addon-intl che fornisce un wrapper semplice da usare per react-intl, inclusa una funzione di cambio lingua per testare il tuo componente in tutte le lingue fornite.

react-testing-library

Per utilizzare react-intl con react-testing-library, dovrai integrare alcune funzioni helper nel flusso di testing.

Puoi consultare la documentazione.

Per creare una soluzione generica, possiamo creare una funzione render personalizzata utilizzando l'opzione wrapper come spiegato nella pagina di setup.
La nostra funzione render personalizzata può essere così definita:

// 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')
})