Pregunta

Estoy usando Node.js con Felixge's node-mysql cliente. No estoy usando un ORM.

Estoy probando con votos y quiero poder burlarse de mi base de datos, posiblemente usando Sinon. Ya que realmente no tengo un dal per se (aparte de node-mysql), No estoy realmente seguro de cómo hacerlo. Mis modelos son en su mayoría simples crud con muchos Getters.

¿Alguna idea sobre cómo lograr esto?

¿Fue útil?

Solución

Con Sinon, puedes poner un simulacro o un trozo alrededor de un módulo completo. Por ejemplo, supongamos que el mysql El módulo tiene una función query:

var mock;

mock = sinon.mock(require('mysql'))
mock.expects('query').with(queryString, queryParams).yields(null, rows);

queryString, queryParams son la entrada que espera. rows es la salida que espera.

Cuando su clase bajo prueba ahora requiere mysql y llama al query Método, será interceptado y verificado por Sinón.

En su sección de expectativas de prueba debe tener:

mock.verify()

Y en su desmontaje debe restaurar MySQL a la funcionalidad normal:

mock.restore()

Otros consejos

Puede ser una buena idea abstraer su base de datos en su propia clase que usa MySQL. Luego, puede pasar la instancia de esa clase a los constructores de su modelo en lugar de que lo carguen usando request ().

Con esta configuración, puede pasar una instancia de DB simulada a sus modelos dentro de sus archivos de prueba unitarios.

Aquí hay un pequeño ejemplo:

// db.js
var Db = function() {
   this.driver = require('mysql');
};
Db.prototype.query = function(sql, callback) {
   this.driver... callback (err, results);
}
module.exports = Db;

// someModel.js
var SomeModel = function (params) {
   this.db = params.db
}
SomeModel.prototype.getSomeTable (params) {
   var sql = ....
   this.db.query (sql, function ( err, res ) {...}
}
module.exports = SomeModel;

// in app.js
var db = new (require('./db.js'))();
var someModel = new SomeModel ({db:db});
var otherModel = new OtherModel ({db:db})

// in app.test.js
var db = {
   query: function (sql, callback) { ... callback ({...}) }
}
var someModel = new SomeModel ({db:db});

No estoy completamente familiarizado con Node.js, pero en un sentido de programación tradicional, para lograr pruebas como esa, necesitaría abstraer del método de acceso a datos. ¿No podrías crear una clase DAL como:

var DataContainer = function () {
}

DataContainer.prototype.getAllBooks = function() {
    // call mysql api select methods and return results...
}

Ahora en el contexto de una prueba, parche su clase GetAllbooks durante la inicialización como:

DataContainer.prototype.getAllBooks = function() {
    // Here is where you'd return your mock data in whatever format is expected.
    return [];
}

Cuando se llama al código de prueba, GetAllbooks será reemplazado por una versión que devuelve datos simulados en lugar de llamar a MySQL. Una vez más, esta es una visión general aproximada ya que no estoy completamente familiarizado con Node.js

Terminé comenzando con la respuesta de @Kgilpin y terminé con algo como esto para probar MySQL en una lambda de AWS:

const sinon = require('sinon');
const LambdaTester = require('lambda-tester');
const myLambdaHandler = require( '../../lambdas/myLambda' ).handler;
const mockMysql = sinon.mock(require('mysql'));
const chai = require('chai');
const expect = chai.expect;

describe('Database Write Requests', function() {

 beforeEach(() => {
   mockMysql.expects('createConnection').returns({
     connect: () => {
       console.log('Succesfully connected');
     },
     query: (query, vars, callback) => {
       callback(null, succesfulDbInsert);
     },
     end: () => {
       console.log('Connection ended');
     }
   });

 });
 after(() => {
   mockMysql.restore();
 });

 describe( 'A call to write to the Database with correct schema', function() {

   it( 'results in a write success', function() {

     return LambdaTester(myLambdaHandler)
       .event(anObject)
       .expectResult((result) => {
         expect(result).to.equal(succesfulDbInsert);
       });
   });
 });


 describe( 'database errors', function() {

   before(() => {
     mockMysql.expects('createConnection').returns({
       connect: () => {
         console.log('Succesfully connected');
       },
       query: (query, vars, callback) => {
         callback('Database error!', null);
       },
       end: () => {
         console.log('Connection ended');
       }
     });
   });

   after(() => {
     mockMysql.restore();
   });

   it( 'results in a callback error response', function() {


     return LambdaTester(myLambdaHandler)
       .event(anObject)
       .expectError((err) => {
         expect(err.message).to.equal('Something went wrong');
       });
   });
 });
});

No quería ninguna conexión de base de datos real, así que me burlé manualmente de todas las respuestas MySQL.
Agregando otra función a .returns Puede burlarse de cualquier método de createConnection.

Puede burlarse de las dependencias externas utilizando horaa

Y también creo que el nodo de Felixge módulo sandboxed También puede hacer algo similar.

Entonces, usando el mismo contexto de Kgilpin, en Horaa se vería algo así como:

var mock = horaa('mysql');
mock.hijack('query', function(queryString, queryParam) {
    // do your fake db query (e.g., return fake expected data)
});

//SUT calls and asserts

mock.restore('query');

Dado que el uso del controlador MySQL requiere que primero cree una conexión y use API del controlador de conexión devuelto: necesita un enfoque de dos pasos.

Hay dos maneras de hacerlo.

Trotar la createconnection, y haga que devuelva una conexión tropezada

Durante la configuración:

const sinon = require('sinon');
const mysql = require('mysql');
const {createConnection} = mysql;
let mockConnection;
sinon.stub(mysql, 'createConnection').callsFake((...args) => {
    mockConnection = sinon.stub(createConnection.apply(mysql, args))
      .expects('query').withArgs(.... )//program it how you like :)
    return mockConnection;
})

const mockConnectionFactory = 
  sinon.stub(mysql)
  .expects('createConnection')

Durante el desmontaje:

mysql.createConnection.restore();

Tenga en cuenta que aquí el query El método se burla en una instancia y no tiene implicaciones en el mecanismo subyacente, por lo que solo el createConnection debe ser restaurado.

Trochando el método .Query en el prototipo de conexión

Esta técnica es un poco más complicada, porque la mysql El conductor no expone oficialmente su conexión para la importación. (Bueno, podría importar solo el módulo que implementa la conexión, pero no hay garantía de que ninguna refactorización no lo mueva desde allí). Entonces, para obtener una referencia al prototipo, generalmente creo una conexión y atravieso la cadena de prototipo del constructor:

Por lo general, lo hago en una línea, pero lo desglosaré en pasos y lo explicaré aquí:

Durante la configuración:

const realConnection = mysql.createConnection({})
const mockTarget = realConnection.constructor.prototype;
//Then - brutally
consdt mock = sinon.mock(mockTarget).expect('query'....
//OR - as I prefer the surgical manner
sinon.stub(mockTarget, 'query').expect('query'....

Durante el desmontaje

//brutal
mock.restore()
// - OR - surgical:
mockTarget.query.restore()

Tenga en cuenta que no nos burlamos del createConnection Método aquí. Todas las validaciones de parámetro de conexión aún se producirán (lo que quiero que sucedan. Aspiro a trabajar con piezas auténticas máximas, por lo tanto, simule el mínimo absoluto requerido para obtener una prueba rápida). Sin embargo, el query se burla del prototipo y debe restaurarse.

También tenga en cuenta que si trabaja quirúrgicamente, el verify estará en el método burlado, no en el mocktarget.

Aquí hay un buen recurso al respecto: http://devdocs.io/sinon~6-stubs/

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top