No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Implementar provider mock

9/16
Recursos

Aportes 29

Preguntas 17

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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

Formas de crear Mock Functions

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

  • jest.fn(): Este método es la forma principal de crear una función de imitación, si declaramos la función de imitación de esta manera, no es obligatorio añadir una implementación (conocida como la función que espiamos también), pero como es normal añadimos la implementación, y para ello, tenemos que pasar la función como parámetro del método.
    /
    Ejemplo:
    /
const mockFunction = jest.fn((a,b) => {
  return a + b
})

/

  • jest.spyOn(): Este método es otra forma de crear una función mock, sin embargo, este se enfoca en mockear los métodos de alguna instancia creada como de su clase principal, y este método solo puede ser usado para ellas, cabe mencionar que no podemos usar este método para el modelo de la clase original, solo para sus instancias, este método recibe dos parámetros, el primero es la instancia declarada como de la clase principal y el segundo es el nombre del método que vamos a espiar.
    /
    Ejemplo:
    /
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)

})

/

  • jest.mock(): Este método sirve para burlarse automáticamente de todas las funciones (sólo funciones autosuficientes, no métodos de alguna clase) que tenga un determinado fichero, sin necesidad de burlarse de todas y cada una de las funciones de ese determinado fichero, este método recibe dos parámetros (el segundo parámetro no es obligatorio), el primero es el fichero cuyas funciones queremos burlar, y el segundo es una función que se ejecutará en lugar de todas las funciones que hayamos definido dentro del otro fichero y que queramos probar, si sólo utilizamos el primer parámetro, entonces cuando utilicemos las funciones del fichero importado, éstas no devolverán nada, ya que las funciones fueron burladas, y si queremos obtener un determinado valor después de utilizar las funciones del fichero importado, tenemos que utilizar el segundo parámetro, pero éste tiene que devolver una función burlada utilizando jest. fn().
    /
    Ejemplo 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()

})

/

  • jest.createMockFromModule(): Este método (también conocido bajo el alias .genMockFromModule()), nos permite transformar las funciones burladas de otro fichero por jest. mock() en funciones normales, de esta manera, serán funciones normales o funciones no burladas y funciones burladas al mismo tiempo, lo que significa que podemos manejarlas como si fueran funciones normales, y además utilizar las propiedades y métodos de una función burlada, esto es útil ya que cuando burlamos todas las funciones de un determinado fichero, estas funciones no pueden ser utilizadas como funciones normales, por ejemplo, esto hace que no obtengamos lo que devuelven las funciones burladas por sí mismas, ya que al ser burladas, este método solo debe recibir un parámetro, este parámetro es el archivo cuyas funciones queremos burlar, entonces para des-bloquear la función de ese archivo, tenemos que invocar esas funciones, y usarlas como variables, y luego debemos usar jest. fn(), y como parámetro de jest.fn(), tenemos que implementar el código de la función original que fue mockeada, y ahora, podemos usar las funciones como si fueran funciones normales y como funciones mockeadas, además de lo que dijimos anteriormente, si estamos exportando diferentes tipos de datos de un determinado archivo, también podemos usarlos en otro archivo, al usar este método, sólo necesitamos llamar a esos datos, pero el método trata los diferentes tipos de variables de diferentes maneras, aquí explicamos eso:
    /
    Función: Crea una nueva función, y esta será burlada, por lo que los parámetros de dicha función serán eliminados y por lo tanto cuando intentemos acceder a los parámetros, estos no aparecerán, y cuando la función lo sea, esta devolverá undefined. Estos cambios también se aplican a las funciones async.
    /
    Clase: Crea una nueva clase, se mantendrá la estructura de la clase original, pero se burlarán todas las propiedades y los métodos de sus instancias, por lo tanto lo que ocurría con la función normal al ser burlada también ocurrirá con los métodos de la clase burlada.
    /
    Objeto: Crea un nuevo objeto, que será exactamente igual al objeto original, por lo que se mantienen las claves del objeto y se burlan los valores de las claves.
    /
    Array: Crea un nuevo array vacío, ignorando el original.
    /
    Primitiva: Crea una nueva variable con el mismo valor primitivo que la variable original.
    /
    Ejemplo 1:
    /
// 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);
    }) 

Mount vs Shallow

Les recomiendo esta lectura sobre las diferencias entre mount y shallow

Mount vs 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