Desarrollo de un equipo para consultar datos de la base de datos.

Actualmente estoy involucrado en la implementación de la interacción con el proveedor de servicios KYC. Como de costumbre, nada cósmico. Solo necesita seleccionar de su base de datos un conjunto bastante grande de copias de varios registros, subirlos al proveedor de servicios y pedirle al proveedor de registros que los revise.


La etapa inicial de procesamiento contiene una docena de operaciones idénticas con el envío de solicitudes para recuperar datos de un usuario específico de varias tablas de bases de datos. Se supone que, en este caso, una parte bastante grande del código se puede reutilizar como una abstracción de Request . Trataré de sugerir cómo se podría usar esto. Escribiré la primera prueba:


 describe('Request', () => { const Request = require('./Request'); it('execute should return promise', () => { const request = new Request(); request.execute().then((result) => { expect(result).toBeNull(); }); }); }); 

Se ve bastante bien? Quizás imperfecto, pero a primera vista parece que Request es esencialmente un que devuelve Promise con el resultado. A partir de esto, es muy posible comenzar. Dibujaré el código para poder ejecutar la prueba.


 class Request { constructor(){} execute(){ return new Promise((resolve, reject) => { resolve(null); }); } } module.exports = Request; 

npm test y observo el punto verde de la prueba completada en la consola.


Entonces Tengo una solicitud y se puede ejecutar. En realidad, sin embargo, tendré que informar de alguna manera mi solicitud sobre en qué tabla debe buscar los datos necesarios y qué criterios deben cumplir estos datos. Intentaré escribir una nueva prueba:


 it('should configure request', () => { const options = { tableName: 'users', query: { id: 1 } }; request.configure(options); expect(request.options).toEqual(options); }); 

Ok? En mi opinión, bastante. Como ya tengo dos pruebas que usan una instancia de la variable de request , inicializaré esta variable en un método especial que se ejecuta antes de que comience cada prueba. Por lo tanto, en cada prueba, tendré una nueva instancia del objeto de solicitud:


 let request = null; beforeEach(() => { request = new Request(); }); 

Implemento esta funcionalidad en la clase de solicitud, le agrego un método que guarda la configuración en la variable de instancia de clase, como lo demuestra la prueba.


 configure(options){ this.options = options; } 

Ejecuté las pruebas y ahora veo dos puntos verdes. Dos de mis pruebas se completaron con éxito. Sin embargo Sin embargo, se supone que mis consultas se dirigirán a la base de datos. Ahora probablemente valga la pena intentar ver de qué lado la solicitud recibirá información sobre la base de datos. Volveré a las pruebas y escribiré un código:


 const DbMock = require('./DbMock'); let db = null; beforeEach(() => { db = new DbMock(); request = new Request(db); }); 

Me parece que una versión tan clásica de inicialización a través del constructor satisface completamente mis requisitos actuales.


Naturalmente, no voy a usar las pruebas unitarias para usar la interfaz con la base de datos MySQL real con la que trabaja nuestro proyecto. Por qué Porque:


  1. Si en lugar de mí, uno de mis colegas necesitará trabajar en esta parte del proyecto y realizar pruebas unitarias antes de que puedan hacer algo, tendrán que dedicar tiempo y esfuerzo a instalar y configurar su propia instancia de servidor MySQL.
  2. El éxito de las pruebas unitarias dependerá de la exactitud del llenado preliminar de datos utilizado por la base de datos del servidor MySQL.
  3. El tiempo para ejecutar pruebas usando la base de datos MySQL será significativamente más largo.

Esta bien ¿Y por qué, por ejemplo, no utilizar ninguna base de datos en la memoria en las pruebas unitarias? Funcionará rápidamente y el proceso de su configuración e inicialización puede automatizarse. Eso es todo cierto, pero por el momento no veo ninguna ventaja al usar esta herramienta adicional. Me parece que mis necesidades actuales son más rápidas y económicas (no es necesario dedicar tiempo a estudiar) usando clases y métodos de y , que solo simularán el comportamiento de las interfaces que se supone que se usarán en condiciones de combate.


Por cierto En combate, sugiero usar estantería junto con knex . Por qué Debido a que siguiendo la documentación sobre la instalación, configuración y uso de estas dos herramientas, logré crear y ejecutar una consulta a la base de datos en unos minutos.


¿Qué se sigue de esto? De esto se deduce que debo modificar el código de la clase Request para que la ejecución de la solicitud coincida con las interfaces exportadas por mis herramientas de combate. Entonces, el código debería verse así:


 class Request { constructor(db){ this.db = db; } configure(options){ this.options = options; } execute(){ const table = this.db.Model.extend({ tableName: this.options.tableName }); return table.where(this.options.query).fetch(); } } module.exports = Request; 

Haré las pruebas y veré qué pasa. Si DbMock supuesto, no tengo el módulo DbMock , así que lo primero que hago es implementar un código auxiliar para él:


 class DbMock { constructor(){} } module.exports = DbMock; 

Haré las pruebas nuevamente. Que ahora La princesa Jasmine me dice que mi DbMock no implementa la propiedad Model . Trataré de llegar a algo:


 class DbMock { constructor(){ this.Model = { extend: () => {} }; } } module.exports = DbMock; 

Ejecutando las pruebas nuevamente. Ahora el error es que en mi prueba unitaria, ejecuto la consulta sin establecer primero sus parámetros utilizando el método de configure . Arreglo esto:


 const options = { tableName: 'users', query: { id: 1 } }; it('execute should return promise', () => { request.configure(options); request.execute().then((result) => { expect(result).toBeNull(); }); }); 

Como ya he usado una instancia de la variable de options en dos pruebas, la puse en el código de inicialización de todo el conjunto de pruebas y ejecuté las pruebas nuevamente.


Como se esperaba, el método extend , las propiedades del Model , de la clase DbMock nos devolvieron undefined , por lo tanto, naturalmente, nuestra solicitud no tiene forma de llamar al método where .


Ya entiendo que la propiedad Model de la clase DbMock debe implementarse fuera de la clase DbMock . En primer lugar, debido al hecho de que la implementación de necesaria para que se ejecuten las pruebas existentes, requerirá demasiados ámbitos anidados al inicializar la propiedad Model directamente en la clase DbMock . Será completamente imposible de leer y entender ... Y esto, sin embargo, no me detendrá de tal intento, porque quiero asegurarme de que todavía tengo la oportunidad de escribir solo unas pocas líneas de código y hacer que las pruebas se ejecuten con éxito.


Entonces Inhala, exhala, los débiles del corazón salen de la habitación. Complementando la implementación del constructor DbMock . Ta-daaaammmmm ...


 class DbMock { constructor(){ this.Model = { extend: () => { return { where: () => { return { fetch: () => { return new Promise((resolve, reject) => { resolve(null); }); } }; } }; } }; } } module.exports = DbMock; 

Estaño! Sin embargo, con mano firme, ejecute las pruebas y asegúrese de que Jasmine nuevamente nos muestre los puntos verdes. Y eso significa que todavía estamos en el camino correcto, aunque algo se ha hinchado de manera inapropiada.


Que sigue Se puede ver a simple vista que la propiedad Model de una pseudo-base de datos debe implementarse como algo completamente separado. Aunque de manera imprevista, no está claro cómo debe implementarse.


Pero estoy seguro de que almacenaré los registros en esta pseudo-base de datos ahora mismo en las matrices más comunes. Y dado que para las pruebas existentes solo necesito simular la tabla de users , para empezar implementaré una matriz de usuarios, con un registro. Pero primero, escribiré una prueba:


 describe('Users', () => { const users = require('./Users'); it('should contain one user', () => { expect(Array.isArray(users)).toBeTruthy(); expect(users.length).toEqual(1); const user = users[0]; expect(user.Id).toEqual(1); expect(user.Name).toEqual('Jack'); }); }); 

Corro pruebas. Me aseguro de que no pasen, e implemento mi contenedor simple con el usuario:


 const Users = [ { Id: 1, Name: 'Jack' } ]; module.exports = Users; 

Ahora se realizan las pruebas, y se me ocurre que semánticamente el Model , en el paquete de bookshell , es el proveedor de la interfaz para acceder al contenido de la tabla en la base de datos. No en vano, pasamos un objeto con un nombre de tabla al método extend . Por qué se llama extend y no, por ejemplo, get , no lo sé. Quizás esto sea solo una falta de conocimiento sobre la API de la bookshell .


Bueno, que Dios lo bendiga, por ahora tengo una idea en mi cabeza sobre el tema de la siguiente prueba:


 describe('TableMock', () => { const container = require('./Users'); const Table = require('./TableMock'); const users = new Table(container); it('should return first item', () => { users.fetch({ Id: 1 }).then((item) => { expect(item.Id).toEqual(1); expect(item.Name).toEqual('Jack'); }); }); }); 

Como en este momento necesito una implementación que solo simule la funcionalidad de un controlador de almacenamiento real, nombro las clases en consecuencia, agregando el sufijo Mock :


 class TableMock { constructor(container){ this.container = container; } fetch() { return new Promise((resolve, reject) => { resolve(this.container[0]); }); } } module.exports = TableMock; 

Pero fetch no fetch el único método que pretendo usar en la versión de combate, así que agrego una prueba más:


 it('where-fetch chain should return first item', () => { users.where({ Id: 1 }).fetch().then((item)=> { expect(item.Id).toEqual(1); expect(item.Name).toEqual('Jack'); }); }); 

El lanzamiento del cual, como se esperaba, me muestra un mensaje de error. Así que complemente la implementación de TableMock con el método where :


 where(){ return this; } 

Ahora se realizan las pruebas y puede pasar al tema de la implementación de la propiedad Model en la clase DbMock . Como ya sugerí, este será un determinado proveedor de instancias de TableMock tipo TableMock :


 describe('TableMockMap', () => { const TableMock = require('./TableMock'); const TableMockMap = require('./TableMockMap'); const map = new TableMockMap(); it('extend should return existent TableMock', () => { const users = map.extend({tableName: 'users'}); expect(users instanceof TableMock).toBeTruthy(); }); }); 

¿Por qué TableMockMap , porque semánticamente esto es todo? Justo en lugar del nombre del método get , se usa el nombre del método extendido.


Dado que la prueba falla, realizamos una implementación:


 const Users = require('./Users'); const TableMock = require('./TableMock'); class TableMockMap extends Map{ constructor(){ super(); this.set('users', Users); } extend(options){ const container = this.get(options.tableName); return new TableMock(container); } } module.exports = TableMockMap; 

Ejecute las pruebas y vea seis puntos verdes en la consola. La vida es bella


En este momento me parece que ya puedes deshacerte de la inicialización en el constructor de la clase DbMock , usando el maravilloso TableMockMap . No lo pospondremos, especialmente porque ya sería bueno tomar el té. La nueva implementación es deliciosamente elegante:


 const TableMockMap = require('./TableMockMap'); class DbMock { constructor(){ this.Model = new TableMockMap(); } } module.exports = DbMock; 

Ejecute las pruebas ... y ¡ay! Nuestra prueba más importante cae. Pero esto es incluso bueno, porque era un trozo de prueba y ahora solo tenemos que arreglarlo:


 it('execute should return promise', () => { request.configure(options); request.execute().then((result) => { expect(result.Id).toEqual(1); expect(result.Name).toEqual('Jack'); }); }); 

Pruebas completadas con éxito. Y ahora puede tomar un descanso y luego volver a finalizar el código de solicitud resultante, porque todavía está muy, muy lejos de ser perfecto, pero incluso de una interfaz fácil de usar, a pesar de que los datos que lo usan son las bases ya se pueden recibir.

Source: https://habr.com/ru/post/es436228/


All Articles