J'écris des tests Jest pour mon code React et j'espère utiliser/tester les contrôles PropType. Je suis assez nouveau dans l'univers Javascript. J'utilise npm pour installer react-0.11.2
et avoir un simple:
var React = require('react/addons');
Dans mes tests. Mon test ressemble assez à l'exemple du didacticiel jest/react avec du code comme:
var eventCell = TestUtils.renderIntoDocument(
<EventCell
slot={slot}
weekId={weekId}
day={day}
eventTypes={eventTypes}
/>
);
var time = TestUtils.findRenderedDOMComponentWithClass(eventCell, 'time');
expect(time.getDOMNode().textContent).toEqual('19:00 ');
Cependant, il semble que les vérifications PropType dans le composant EventCell
ne soient pas déclenchées. Je comprends que les vérifications ne sont exécutées qu'en mode Développement, mais j'ai également pensé que le fait d'obtenir react
via npm vous donnait la version de développement. Les vérifications se déclenchent dans mon navigateur lorsque je crée le composant avec watchify.
Qu'est-ce que je rate?
Le problème sous-jacent est Comment tester console.log
?
La réponse courte est que vous devez remplacer le console.{method}
pendant la durée du test. L'approche courante consiste à utiliser espions . Dans ce cas particulier, vous pouvez utiliser stubs pour empêcher la sortie.
Voici un exemple d'implémentation utilisant Sinon.js (Sinon.js fournit des espions, des talons et des simulateurs autonomes):
import {
expect
} from 'chai';
import DateName from './../../src/app/components/DateName';
import createComponent from './create-component';
import sinon from 'sinon';
describe('DateName', () => {
it('throws an error if date input does not represent 12:00:00 AM UTC', () => {
let stub;
stub = sinon.stub(console, 'error');
createComponent(DateName, {date: 1470009600000});
expect(stub.calledOnce).to.equal(true);
expect(stub.calledWithExactly('Warning: Failed propType: Date unix timestamp must represent 00:00:00 (HH:mm:ss) time.')).to.equal(true);
console.error.restore();
});
});
Dans cet exemple, le composant DataName
générera une erreur lorsqu'il sera initialisé avec une valeur d'horodatage qui ne représente pas une date précise (12:00:00 AM).
Je stoppe le console.error
méthode (C'est ce que le module Facebook warning
utilise en interne pour générer l'erreur). Je m'assure que le stub a été appelé une fois et avec exactement un paramètre représentant l'erreur.
Intro
La réponse de @Gajus m'a certainement aidé (donc, merci Gajus ). Cependant, j'ai pensé que je fournirais une réponse qui:
Résumé
Comme l'approche suggérée ici par Gajus et ailleurs par d'autres, l'approche de base que je suggère est également de déterminer si oui ou non console.error
est utilisé par React en réponse à une valeur d'accessoire de test inacceptable . Plus précisément, cette approche consiste à effectuer les opérations suivantes pour chaque valeur d'accessoire de test:
console.error
(pour garantir les appels antérieurs à console.error
n'interfèrent pas),console.error
a été renvoyé comme prévu. La fonction testPropTypes
Le code suivant peut être placé dans le test ou en tant que module/fichier séparé/importé/requis:
const testPropTypes = (component, propName, arraysOfTestValues, otherProps) => {
console.error = jest.fn();
const _test = (testValues, expectError) => {
for (let propValue of testValues) {
console.error.mockClear();
React.createElement(component, {...otherProps, [propName]: propValue});
expect(console.error).toHaveBeenCalledTimes(expectError ? 1 : 0);
}
};
_test(arraysOfTestValues[0], false);
_test(arraysOfTestValues[1], true);
};
Appel de la fonction
Tout test examinant propTypes
peut appeler testPropTypes
en utilisant trois ou quatre paramètres :
component
, le composant React qui est modifié par l'hélice;propName
, la chaîne nom de l'hélice sous test;arraysOfTestValues
, un tableau de tableaux de toutes les valeurs de test souhaitées de l'hélice à tester: facultativement, otherProps
, un objet contenant des paires nom/valeur d'accessoire pour tout autre accessoire requis de ce composant.
L'objet otherProps
est nécessaire pour garantir que React ne fait pas d'appels non pertinents à console.error
parce que d'autres accessoires requis manquent par inadvertance. Incluez simplement une seule valeur acceptable pour tous les accessoires requis, par exemple {requiredPropName1: anyAcceptableValue, requiredPropName2: anyAcceptableValue}
.
Logique de fonction
La fonction effectue les opérations suivantes:
Il met en place une maquette de console.error
qui est ce que React utilise pour signaler les accessoires de type incorrect.
Pour chaque sous-tableau de valeurs d'accessoire de test à condition qu'il boucle à travers chaque valeur d'accessoire de test dans chaque sous-réseau pour tester le type d'hélice:
Dans la boucle pour chaque valeur de test individuelle, le console.error
mock est d'abord effacé afin que tout message d'erreur détecté puisse être supposé provenir de ce test.
Une instance du composant est ensuite créée à l'aide de la valeur d'accessoire de test ainsi que de tout autre accessoire requis nécessaire qui n'est pas actuellement testé.
Enfin, une vérification est effectuée pour voir si un avertissement a été déclenché , ce qui devrait se produire si votre test a essayé de créer un composant en utilisant un accessoire inapproprié ou manquant .
Test des accessoires facultatifs par rapport aux accessoires requis
Notez que l'attribution de null
(ou undefined
) à une valeur d'accessoire est, du point de vue de React, essentiellement la même chose que de ne fournir aucune valeur pour cet accessoire. Par définition, cela est acceptable pour un accessoire optionnel mais inacceptable pour un accessoire requis. Ainsi, en plaçant null
dans le tableau de valeurs acceptables ou inacceptables, vous testez si cet accessoire est facultatif ou requis respectivement .
Exemple de code
MyComponent.js (juste le propTypes
):
MyComponent.propTypes = {
myProp1: React.PropTypes.number, // optional number
myProp2: React.PropTypes.oneOfType([ // required number or array of numbers
React.PropTypes.number,
React.PropTypes.arrayOf(React.PropTypes.number)
]).isRequired
MyComponent.test.js:
describe('MyComponent', () => {
it('should accept an optional number for myProp1', () => {
const testValues = [
[0, null], // acceptable values; note: null is acceptable
['', []] // unacceptable values
];
testPropTypes(MyComponent, 'myProp1', testValues, {myProp2: 123});
});
it('should require a number or an array of numbers for myProp2', () => {
const testValues = [
[0, [0]], // acceptable values
['', null] // unacceptable values; note: null is unacceptable
];
testPropTypes(MyComponent, 'myProp2', testValues);
});
});
Limitation de cette approche (IMPORTANT)
Il existe actuellement des limitations importantes sur la façon dont vous pouvez utiliser cette approche qui, si elle est dépassée, pourrait être la source de quelques bogues de test difficiles à tracer. Les raisons et les implications de ces limitations sont expliquées dans cet autre SO question/réponse . En résumé, pour les types d'accessoires simples, comme pour myProp1
, vous pouvez tester autant de valeurs d'accessoires non -null
de test que vous le souhaitez tant qu'elles sont toutes de types de données différents. Pour certains types d'accessoires complexes, comme pour myProp2
, vous pouvez uniquement tester une valeur unique non -null
prop inacceptable de tout type. Voir cette autre question/réponse pour une discussion plus approfondie.
Se moquer console.error
ne convient pas aux tests unitaires! @AndrewWillems lié à n autre SO question dans un commentaire ci-dessus qui décrit les problèmes avec cette approche.
Consultez ce problème sur facebook/prop-types pour une discussion sur la capacité de cette bibliothèque à lancer au lieu de consigner les erreurs propType (au moment de la rédaction, ce n'est pas pris en charge).
J'ai publié une bibliothèque d'aide pour fournir ce comportement dans l'intervalle, check-prop-types . Vous pouvez l'utiliser comme ceci:
import PropTypes from 'prop-types';
import checkPropTypes from 'check-prop-types';
const HelloComponent = ({ name }) => (
<h1>Hi, {name}</h1>
);
HelloComponent.propTypes = {
name: PropTypes.string.isRequired,
};
let result = checkPropTypes(HelloComponent.propTypes, { name: 'Julia' }, 'prop', HelloComponent.name);
assert(`result` === null);
result = checkPropTypes(HelloComponent.propTypes, { name: 123 }, 'prop', HelloComponent.name);
assert(`result` === 'Failed prop type: Invalid prop `name` of type `number` supplied to `HelloComponent`, expected `string`.');
Un nouveau package jest-prop-type-error est simple à ajouter et échoue sur les erreurs PropType
:
Installer via:
yarn add -D jest-prop-type-error
Ajoutez ensuite ce qui suit à votre package.json
's setupFiles
dans la section jest
:
"setupFiles": [
"jest-prop-type-error"
]
Étant donné que ReactJS n'enverra que des avertissements à la console mais ne lancera pas réellement d'erreur, je teste les valeurs des accessoires de cette manière:
var myTestElement = TestUtils.renderIntoDocument(
<MyTestElement height={100} /> );
it("check MyTestElement props", function() {
expect( typeof myTestElement.props.height ).toEqual ( 'number' );
});