Mocking
Clase 18 de 27 • Curso de Introducción al Testing con JavaScript
Contenido del curso
Clase 18 de 27 • Curso de Introducción al Testing con JavaScript
Contenido del curso
Iván Antonio Bustos Calderón
Miguel Angel Reyes Moreno
Aram Missael Guzmán Boiso
Nicolás Sañudo
Nicolás Sañudo
Jose Bernabe Rios Nuñez
Samuel Gómez Balderas
Tomás André Peñaloza Avilés
Fabian Andres
Diego Vergara
Nicolas Molina
Eduardo Kiriakos Piazza
Nicolas Molina
Salvador Santander Gutiérrez
Aldo Abril Ricalde
Samuel Gómez Balderas
Carlos Andrés Moreno Jimenez
Manuel Roa Ojeda
JUAN JOSE HERNANDEZ MUÑOZ
Manuel Roa Ojeda
// Creamos el libro fake que será devuelto por el mock
const fakeBooks = [ { _id: 1, name: ‘Harry Potter’, }, ];
// Suplanta a nuestra mongoLib real
const mongoLibStub = { getAll: () ⇒ […fakeBooks], c. reate: () ⇒ {}, }
// Suplantamos al método
jest.mock(’…/lib/mongo.lib’, () ⇒ jest.fn().mockImplementation(() ⇒ mongoLibStub))
Conforme avanza el curso me siento poderoso :D
Mocking
Suplantando MongoLib
Se utilizo el método jest.mock, al cual se le indica la clase que va suplantar
jest.mock('../lib/mongo.lib', () => jest.fn().mockImplementation(() => MongoLibStub));
Para eso se crea la clase MongoLibStub, en la cual se contendrán los métodos getAll, y create los cuales servirán para retornar un resultado similar a la clase original.
const MongoLibStub = { getAll: () => fakeBooks, create: () => { }, };
fakeBooks hace referencia a un arreglo de objetos de tipo Book ficticios, los cuales serán retornados por el método getAll.
const fakeBooks = [ { _id: '1', name: 'Harry Potter', }, { _id: '2', name: 'Principito', }, ];
Con esto ya se suplantaría el comportamiento de la clase MongoLib y sus métodos, es importante mencionar que la clase MongoLibStub puede crecer con forme se necesite.
Se aconseja como buena practica utilizar el método clearAllMocks() de jest para hacer una limpia de los mocks creados anteriormente.
jest.clearAllMocks();
Hice exactamente lo mismo que hizo el profesor en el video y tengo este error:
npm run test > api@1.0.0 test > jest FAIL src/services/books.service.test.js Test for BooksService test for getBooks × should return a list book (1 ms) ● Test for BooksService › test for getBooks › should return a list book TypeError: MongoLib is not a constructor 4 | constructor() { 5 | this.collection = 'books'; > 6 | this.mongoDB = new MongoLib(); | ^ 7 | } 8 | 9 | getBooks(query) { at new BooksService (src/services/books.service.js:6:20) at Object.<anonymous> (src/services/books.service.test.js:22:15) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.656 s, estimated 1 s Ran all test suites.
// books.service.test.js const BooksService = require('./books.service'); const fakeBooks = [ { _id: 1, name: 'Harry Potter', }, ]; const MongoLibStub = { getAll: () => [...fakeBooks], create: () => {}, }; jest.mock('../lib/mongo.lib', () => { jest.fn().mockImplementation(() => MongoLibStub); }); describe('Test for BooksService', () => { let service; beforeEach(() => { service = new BooksService(); jest.clearAllMocks(); }); describe('test for getBooks', () => { test('should return a list book', async () => { const books = await service.getBooks({}); expect(books.length).toEqual(1); }); }); });
Yo tuve el mismo problema solo debes quitar las {} o agregar return, ya que la función necesita ser retornada. Te quedaría así:
jest.mock('../lib/mongo.lib', () => jest.fn().mockImplementation(() => MongoLibStub) );
Tengo una duda: ¿Por qué manda a llamar a jest.ClearAllMocks() dentro de beforeEach()?
Es decir, el código del profe funciona, sin embargo si yo lo hubiese tenido que realizar, mi lógica me diría que lo pusiera dentro de afterAll por otro lado mi lógica me dice que el código del profe debería fallar y sin embargo no lo hace, no entiendo por qué?
NOTA: Intenté publicar esto como pregunta, pero no me dejó pues me indicaba que la pregunta ya había sido hecha y contestada, sin embargo las respuestas que me ofrecía eran sobre algo totalmente diiferente
En realidad sí tiene mucho sentido... Imagínate que para el segundo test necesitemos un array disitinto de datos mockeados distinto... digamos:
const fakeBooks2 = [ { _id: 'e23JX', titleEnglish: "Lord of the Rings", titleSpanish: "El señor de los Anillos", }, ];
En ese caso, si pusieramos el reseteo de los mocks en un afterAll el mock seguiría usando el fakeBooks anterior para la segunda prueba. Se pone el reseteo de mocks en el beforeEach para que se pueda resetear los mocks antes de cada test y así implementar mocks distintos si así lo requerimos.
Como iplementamos mocks distintos para cada prueba?? esa sí es una pregunta que aún no logro responder. Pero te la comentaré en cuanto lo sepa.
@TomAtomicDev, lo que creo que pregunto @sierra034 es que independientemente de que se cree mocks distintos, el metodo afterAll() deberia poder eliminarlos al finalizar el testeo completo del archivo... Y tambien me quedo esa duda
Sería recomendable replicar la base datos original para hacer pruebas?
Hola, he visto que en algunas empresas si lo hacen para tener un ambiente lo más parecido a producción, pero depende de tu equipo y que tan grande sea la base de datos.
Me parece un código muy complejo para probar... nada? Pregunto, si tenemos un servicio que SOLAMENTE sirve de intermediario para obtener datos de la db ¿Qué sentido tiene aislarlo?
Esto depende del tipo de pruebas que estés haciendo si son Unit tests solo te enfocas en la funcionalidad basé no en terceros y en e2e se hace menos pero por ejemplo si tienes un servicio de un tercero como envíar mails no puedes comprobar que el email llegó por que por ejemplo no puedes ir a la base de datos de Gmail y ver si se envió entonces usas un mock
¿Sería recomendable hacer el mock incluso antes de tener implementado mongolib?
Desde luego que si, desde la perspectiva del TDD, primero haces las pruebas y luego desarrollas la lógica que pasa dichas pruebas.
Tengo una duda: ¿Por qué manda a llamar a jest.ClearAllMocks() dentro de beforeEach()?
Es decir, el código del profe funciona, sin embargo si yo lo hubiese tenido que realizar, mi lógica me diría que lo pusiera dentro de afterAll por otro lado mi lógica me dice que el código del profe debería fallar y sin embargo no lo hace, no entiendo por qué?
EL profe fue claro, pero te lo diré asi, supongamos que en vez de hacer un getBooks , haces un createBook en tus pruebas y quieres que cada una de tus pruebas sean independientes y aisladas unas de las otras lo mas normal es que querias que al iniciar tu prueba la base de datos este limpia y no con informacion de anteriores pruebas al momento de crear un nuevo libro
Sera mejor hacer los test con una base de datos replicada ?? tal vez con datos random ??
Hice un test para createBook!
Mi Stub seria el siguiente:
const MongoLibStub = { getAll: () => [...fakeBooks], create: (collection, object) => ({ id: 1, ...object, }), }; ```y mi test: ```js describe(('test for createBook'), () => { test('should return a book list', async () => { // Arrange const newBook = { name: 'Juramentada', author: 'Brandon Sanderson', }; // Act // En este momento no se usa el MongoLib original sino el Stub const books = await service.createBook(newBook); // Assert expect(books).toEqual({ id: 1, ...newBook }); }); }); ```No se si es redundante o de que otra forma se podria hacer! Recibo feedback
test("should return books", async () => { const books = await service.getBooks(); expect(books).toBeTruthy(); expect(books.length).toBe(1); expect(books[0].name).toBe("harry potter"); });