Le problème:
J'ai un simple composant React que j'utilise pour apprendre à tester les composants avec Jest et Enzyme. Comme je travaille avec des accessoires, j'ai ajouté le module prop-types
pour vérifier les propriétés en développement. prop-types
utilise console.error
pour alerter lorsque les accessoires obligatoires ne sont pas transmis ou lorsque les accessoires sont du type de données incorrect.
Je voulais me moquer de console.error
pour compter le nombre de fois où il a été appelé par prop-types
lorsqu’il est passé dans des accessoires manquants ou mal typés.
En utilisant cet exemple de composant et test simplifié, je m'attendrais à ce que les deux tests se comportent comme tels:
Au lieu de cela, je reçois ceci:
console.error
est supprimée, il est donc clair qu'elle est simulée pour les deux.Je suis sûr qu'il me manque quelque chose d'évident, comme effacer le faux semblant ou autre.
Lorsque j'utilise la même structure par rapport à un module qui exporte une fonction, en appelant console.error
un nombre de fois arbitraire, tout fonctionne.
C'est quand je teste avec enzyme/react que je frappe ce mur après le premier test.
Exemple App.js:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class App extends Component {
render(){
return(
<div>Hello world.</div>
);
}
};
App.propTypes = {
id : PropTypes.string.isRequired,
data : PropTypes.object.isRequired
};
Exemple App.test.js
import React from 'react';
import { mount } from 'enzyme';
import App from './App';
console.error = jest.fn();
beforeEach(() => {
console.error.mockClear();
});
it('component logs two errors when no props are passed', () => {
const wrapper = mount(<App />);
expect(console.error).toHaveBeenCalledTimes(2);
});
it('component logs one error when only id is passed', () => {
const wrapper = mount(<App id="stringofstuff"/>);
expect(console.error).toHaveBeenCalledTimes(1);
});
Note finale: Ouais, il est préférable d'écrire le composant pour générer une sortie conviviale lorsque les accessoires sont manquants, puis effectuez un test. Mais une fois que j'ai trouvé ce comportement, je voulais comprendre ce que je faisais mal pour améliorer ma compréhension. Clairement, il me manque quelque chose.
Tu n'as rien manqué. Il existe un problème connu ( https://github.com/facebook/react/issues/7047 ) concernant les messages d'erreur/d'avertissement manquants.
Si vous modifiez vos scénarios de test ('... lorsque seul l'identifiant est passé' - le premier ',' ... lorsque aucun accessoire n'est transmis '- le second) et ajoutez un tel console.log('mockedError', console.error.mock.calls);
dans vos scénarios de test , vous pouvez voir que le message sur l’identifiant manquant n’est pas déclenché lors du deuxième test.
Compte tenu du comportement expliqué par @DLyman, vous pouvez le faire comme ça:
describe('desc', () => {
let spy = spyConsole();
it('x', () => {
// [...]
});
it('y', () => {
// [...]
});
it('throws [...]', () => {
shallow(<App />);
expect(console.error).toHaveBeenCalled();
expect(spy.console.mock.calls[0][0]).toContain('The prop `id` is marked as required');
});
});
function spyConsole() {
// https://github.com/facebook/react/issues/7047
let spy = {};
beforeAll(() => {
spy.console = jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterAll(() => {
spy.console.mockRestore();
});
return spy;
}
J'ai rencontré un problème similaire, j'avais juste besoin de mettre en cache la méthode d'origine
const original = console.error
beforeEach(() => {
console.error = jest.fn()
console.error('you cant see me')
})
afterEach(() => {
console.log('log still works')
console.error('you cant see me')
console.error.mockClear()
console.error = original
console.error('now you can')
})
Ce que les gars ont écrit ci-dessus est correct. J'ai rencontré un problème similaire et voici ma solution. Il prend également en considération la situation lorsque vous faites une affirmation sur l'objet simulé:
beforeAll(() => {
// Create a spy on console (console.log in this case) and provide some mocked implementation
// In mocking global objects it's usually better than simple `jest.fn()`
// because you can `unmock` it in clean way doing `mockRestore`
jest.spyOn(console, 'log').mockImplementation(() => {});
});
afterAll(() => {
// Restore mock after all tests are done, so it won't affect other test suites
console.log.mockRestore();
});
afterEach(() => {
// Clear mock (all calls etc) after each test.
// It's needed when you're using console somewhere in the tests so you have clean mock each time
console.log.mockClear();
});