Probablemente muchos se hagan esta pregunta:
¿Cuándo utilizar mount y cuándo utilizar shallow?
.
mount --> Cuando necesitas el DOM
shallow --> Solo necesitas algo particular del componente. No ocupas todo el DOM
Bienvenida
Bienvenido al Curso de Jest
¿Qué es un Test? ¿Qué tipos de Test Existen? Jest
Introducción a Jest
Preparación del entorno con Jest
Implementando pruebas para Boolean y Array
Implementando pruebas a promesas
Watch y Coverage
Usando Jest con React
Preparar proyecto
Crear mocks
Implementar provider mock
Snapshot
Probar Actions
Probar Reducers
Probar peticiones fetch
Deploy y CI con Travis
Jest + CI
Probando el proyecto antes de hacer deploy
Recapitulación y cierre
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Oscar Barajas Tavares
Aportes 29
Preguntas 17
Probablemente muchos se hagan esta pregunta:
¿Cuándo utilizar mount y cuándo utilizar shallow?
.
mount --> Cuando necesitas el DOM
shallow --> Solo necesitas algo particular del componente. No ocupas todo el DOM
Shallow permite traer elementos y probarlos como una unidad. Es útil cuando solo necesito algo particular de ese componente y no necesito toda su estructura y elementos del DOM
La prueba solo me funcionó poniendo un
return(
<Provider store={store}>
<Router history={history}>
{props.children}
</Router>
</Provider>
)
por si le pasa a alguien
/
En jest, podemos crear mock functions de tres formas distintas, esto se debe a que aunque todas tienen como objetivo testear funciones, hay diferentes formas de crear funciones en JavaScript.
/
const mockFunction = jest.fn((a,b) => {
return a + b
})
/
class Computer {
constructor(brand, size, price, ram) {
this.brand = brand;
this.size = size;
this.price = price;
this.ram = ram;
}
getInformation(){
return {
brand: this.brand,
size: this.size,
price: this.price,
ram: this.ram,
}
}
setInformation(brand, size, price, ram) {
this.brand = brand;
this.size = size;
this.price = price;
this.ram = ram;
return {
brand: this.brand,
size: this.size,
price: this.price,
ram: this.ram,
}
}
}
const MackBookPro = new Computer('Apple', '3000px', 5000, 36)
const getInformationSpier = jest.spyOn(MackBookPro, 'getInformation')
const setInformationSpier = jest.spyOn(MackBookPro, 'setInformation')
test('testing the methods of the class computer', () => {
expect(MackBookPro.getInformation()).toEqual({
brand: 'Apple',
size: '3000px',
price: 5000,
ram: 36,
})
expect(MackBookPro.setInformation('Toshiba', '2500px', 1000, 20)).toEqual({
brand: 'Toshiba',
size: '2500px',
price: 1000,
ram: 20,
})
expect(getInformationSpier).toHaveBeenCalled()
expect(setInformationSpier).toHaveBeenCalledTimes(1)
})
/
// anotherFile.js
const addition = (a, b) => {
return a + b;
}
const subtraction = (a, b) => {
return a - b;
}
module.exports = {
addition,
subtraction,
}
jest.mock('./anotherFile')
const additionFunction = require('./anotherFile').addition
const subtractionFunction = require('./anotherFile').subtraction
additionFunction(1,2)
additionFunction(3,4)
subtractionFunction(4,3)
subtractionFunction(2,1)
test('testing mathematical functions', () => {
console.log(additionFunction.mock)
console.log(subtractionFunction.mock)
expect(additionFunction).toHaveBeenCalled()
expect(subtractionFunction).toHaveBeenCalled()
})
/
// anotherFile.js
const addition = (a, b) => {
return a + b;
}
const subtraction = (a, b) => {
return a - b;
}
module.exports = {
addition,
subtraction,
}
/
const mathematicalFunctions = jest.createMockFromModule('./anotherFile')
mathematicalFunctions.addition = jest.fn((a, b) => {
return a + b;
})
mathematicalFunctions.subtraction = jest.fn((a, b) => {
return a - b;
})
const additionFunction = mathematicalFunctions.addition
const subtractionFunction = mathematicalFunctions.addition
test('testing an addition function', () => {
console.log(additionFunction(12,23))
console.log(additionFunction.mock)
console.log(subtractionFunction(20,5))
console.log(subtractionFunction.mock)
})
/
Ejemplo 2:
/
const typesOfData = jest.createMockFromModule('./anotherFile');
test('should run example code', () => {
expect(typesOfData.theFunction.name).toEqual('workSomethingOut');
expect(typesOfData.theFunction.length).toEqual(0);
expect(typesOfData.theClass.constructor.name).toEqual('User');
expect(typesOfData.theClass.toGreet.name).toEqual('toGreet');
expect(typesOfData.theObject).toEqual({
brand: 'Apple',
price: 1000,
});
expect(typesOfData.theArray.length).toEqual(0);
expect(typesOfData.theNumber).toEqual(12);
expect(typesOfData.theString).toEqual('RAM');
expect(typesOfData.theBoolean).toEqual(true);
});
/
Saludos.
El curso parece un tutorial en lugar de un curso.
Si tienen el siguiente error:
ProviderMock(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
Lo pude solucionar agregando un return en el ProviderMock, quedando asi:
return (
<Provider store={store}>
<Router history={history}>
{props.children}
</Router>
</Provider>
)
En la última prueba, en la de la función, no es necesario envolver el componente <Product/> con el <ProviderMock />, ya que al hacer “Render” con la funcion mount, se le estan pasando los dato a travez de las props del componente, dejando de lado Redux para adquirir esos datos!
De igual forma al revisar el archivo de <Product/> se puede notar que en ningún lado se esta usando Redux.
Al usar shallow es lo mísmo, mientras le pasemos props al componente se puede prescindir del <ProviderMock/>.
Si se topan el error Invariant failed: Browser history needs a DOM
, pueden usar createMemoryHistory
.
La verdad no estoy seguro si la prueba de simular el click sea del todo cierta o que realmente compruebe el funcionamiento del botón, me explico:
Si comentan la linea de código donde simulan el click igual la prueba sigue siendo exitosa, de hecho cambié el número de veces que se llama la función solo para generar error y pareciera que el agregar la función al evento onClick del boton se considerara como un llamado a la función. También probe usando un expect diferente al del ejercicio. Adjunto código y outputs para verificar entre todos este caso,
test('Test button Add to cart', () => {
const handleAddToCart = jest.fn();
const wrapper = mount(
<ProviderMock>
<Product product={ProductMock} handleAddToCart={handleAddToCart} />
</ProviderMock>,
);
//wrapper.find('button').simulate('click');
//expect(handleAddToCart).toHaveBeenCalledTimes(1);
expect(handleAddToCart.mock.calls.length).toEqual(1);
});
# con cualquiera de los dos expect siempre se llama una vez a la función y el test es positivo sin simular un click
Esta salida es cuando cambio el valor a 2 para verificar cuantos llamados a la función se estan haciendo, como ven sin necesidad de simular el click, ya hay un llamado.
para poder realizar el curso es necesario tener las Sifuentes versiones
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
"jest": "^24.9.0",
Nota
El providerMock sirve para dar conexto a enzime de los keywords usados por redux en la app (como “store”). Corri un test sin provicerMock y da el siguiente error
Could not find “store” in the context of “Connect(Header)”. Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Header) in connect options
Al añadir el mock evitamos este tipo de errores donde nuestra testing library podria no entender el codigo
La diferencia entre shallow y mount es que shallow prueba los componentes de forma aislada de los componentes secundarios que representan, mientras que mount profundiza y prueba los elementos secundarios de un componente. En el caso de shallow, esto significa que si el componente principal representa otro componente que no se procesa, se seguirá procesando una representación superficial en el elemento principal.
tengo este problema, alguien le paso y lo pudo resolver?
ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was `object`.
6 | describe('<Header />', () => {
7 | test('Prueba de Header', () => {
> 8 | const header = shallow(
| ^
9 | <ProviderMock>
10 | <Header />
11 | </ProviderMock>,```
Critica constructiva. El profesor deberia mejorar su forma de explicar. Esta bien que no nos digan todo, pero a veces uno que otro porque de las cosas que hace no estaria mal. Simplemente narra lo que hace y ya sin explicar el porque de las cosas
Nuevamente vuelve a pasar lo mismo con los cursos de React de Platzi, desactualizadísimas, se pierde muchísimo tiempo buscando como solucionar los errores al seguir lo que hace el profesor en los videos. Desmotiva, y relentece el avance.
Si alguien de Platzi lee esto, pónganse a ACTUALIZAR TODOS LOS CURSOS DE REACT.
En el caso de que estuvieran usando Hooks y useContext se haría de la siguiente manera:
.
import React from 'react';
import AppContext from '../context/AppContext';
import {createBrowserHistory} from 'history';
import {Router} from 'react-router-dom';
import useInitialState from '../hooks/useInitialState';
const history = createBrowserHistory()
const ProviderMock = (props) =>{
const state = useInitialState()
return(
<AppContext.Provider value={state}>
<Router history={history}>
{props.children}
</Router>
</AppContext.Provider>
)
}
export default ProviderMock;
Aquí les dejo como estaría estructurado App con AppContext en vez de Redux:
.
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Layout from './components/Layout';
import AppContext from './context/AppContext';
import useInitialState from './hooks/useInitialState';
import Home from './pages/Home';
import Checkout from './pages/Checkout';
import Information from './pages/Information';
import NotFound from './pages/NotFound';
import Payment from './pages/Payment';
import Success from './pages/Success';
const App = () => {
const state = useInitialState()
return (
<AppContext.Provider value={state}>
<BrowserRouter>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/checkout" component={Checkout} />
<Route exact path="/information" component={Information} />
<Route exact path="/payment" component={Payment} />
<Route exact path="/success" component={Success} />
<Route component={NotFound} />
</Switch>
</Layout>
</BrowserRouter>
</AppContext.Provider>
)
}
export default App;
Que interesante que esta este curso 😱
Alguien le ha pasado? Me sale el siguiente error: Unable to resolve path to module ‘history’
Algo a tener en cuenta a la hora de realizar nuestros tests unitarios es que debemos validar el comportamiento deseado.
El código actual tiene un error, y es que ejecuta la función de handleAddToCart
en cuanto se renderiza el componente. Osea que incluso sin hacer click la función es llamada en el test.
Esto se arregla agregando la sintaxis
()=>prop.function()
en el onclick del botón.
Muy complicado seguir el curso, el proyecto no para dar errores por todo lados, se pierde mucho tiempo en ello, deberían actualizarlo a cosas más eficientes y prácticas con vite y vitest, es básicamente el mismo testing de jest pero más eficiente y con menos errores
Para la prueba Product.test.js no es necesario usar el ProviderMock ya que el componente que se està testeando (Product) no està usando Redux. Esto funciona ok:
test('Comprobar el botón de comprar',()=>{
const handleAddToCart = jest.fn();
const wrapper = mount(
// <ProviderMock>
<Product
product={ProductMock}
handleAddToCart={handleAddToCart}
/>
// </ProviderMock>
)
wrapper.find('button').simulate('click')
expect(handleAddToCart).toHaveBeenCalledTimes(1);
})
Les recomiendo esta lectura sobre las diferencias entre mount y shallow
Shallow - Bring elements and test them as a Unit
si tienen problemas con el error ReferenceError: regeneratorRuntime is not defined
o con async / await
, el problema está en la configuracion de babel en el archivo .babelrc
. Se debe definir un target para el preset @babel/preset-env
. En mi caso necesitaba dirigir babel a mi version actual de node, asi que mi .babelrc
quedo asi:
{
"presets": [
["@babel/preset-env", { "targets": { "node": "current" } }],
"@babel/preset-react"
]
}
Que significa lo siguiente?
Me sale un error:
console.error node_modules/prop-types/checkPropTypes.js:20
Warning: Failed prop type: The prop history
is marked as required in Router
, but its value is undefined
.
in Router
Shallow nos permite traer un elemento y probarlo
Función toHaveBeenCalledTimes()
Method “text” is meant to be run on 1 node. 0 found instead. Me da ese error, alguien sabe como lo soluciono?
Alguien pudo pasar los test con el proyecto terminado del curso de Hooks? porque al terminar, los actions quedan en un hook, no en el reducer
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?