Question

J'utilise Node.js avec le client node-mysql de felixge.Je n'utilise pas d'ORM.

Je teste avec Vows et je veux pouvoir me moquer de ma base de données, éventuellement en utilisant Sinon.Étant donné que je n'ai pas vraiment de DAL en soi (à part node-mysql), je ne sais pas vraiment comment s'y prendre.Mes modèles sont pour la plupart de simples CRUD avec beaucoup de getters.

Avez-vous des idées pour y parvenir?

Était-ce utile?

La solution

Avec sinon, vous pouvez mettre une maquette ou un stub autour d'un module entier.Par exemple, supposons que le module mysql ait une fonction query:

var mock;

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

queryString, queryParams sont l'entrée que vous attendez.rows est la sortie que vous attendez.

Lorsque votre classe testée nécessite maintenant mysql et appelle la méthode query, elle sera interceptée et vérifiée par sinon.

Dans la section des attentes de votre test, vous devriez avoir:

mock.verify()

et dans votre démontage, vous devez restaurer mysql à ses fonctionnalités normales:

mock.restore()

Autres conseils

Cela peut être une bonne idée d'abstraire votre base de données dans sa propre classe qui utilise mysql.Ensuite, vous pouvez passer l'instance de cette classe aux constructeurs de votre modèle au lieu de les charger en utilisant require ().

Avec cette configuration, vous pouvez transmettre une instance de base de données fictive à vos modèles dans vos fichiers de test unitaire.

Voici un petit exemple:

// 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});

Je ne suis pas tout à fait familier avec node.js, mais dans un sens de programmation traditionnel, pour réaliser des tests comme celui-là, vous devez faire abstraction de la méthode d'accès aux données.Impossible de créer une classe DAL comme:

var DataContainer = function () {
}

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

Maintenant, dans le cadre d'un test, corrigez votre classe getAllBooks lors de l'initialisation comme:

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

Lorsque le code de test est appelé, getAllBooks sera remplacé par une version qui renvoie des données factices au lieu d'appeler réellement mysql.Encore une fois, ceci est un aperçu approximatif car je ne suis pas tout à fait familier avec node.js

J'ai fini par commencer par la réponse de @ kgilpin et j'ai fini avec quelque chose comme ceci pour tester Mysql dans un AWS Lambda:

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');
       });
   });
 });
});

Je ne voulais pas de connexion réelle à la base de données, alors je me suis moqué de toutes les réponses mysql.
En ajoutant une autre fonction à .returns, vous pouvez vous moquer de n'importe quelle méthode de createConnection.

Vous pouvez simuler des dépendances externes en utilisant horaa

Et je crois aussi que le nœud sandboxed-module de felixge peut également faire quelque chose de similaire.

Donc, en utilisant le même contexte de kgilpin, dans horaa cela ressemblerait à quelque chose comme:

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');

Puisque l'utilisation du pilote mysql vous oblige d'abord à créer une connexion et à utiliser les API du contrôleur de connexion renvoyé - vous avez besoin d'une approche en deux étapes.

Il existe deux façons de procéder.

stubbing de createConnection et lui faire renvoyer une connexion stubbed

Pendant la configuration:

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')

Pendant le démontage:

mysql.createConnection.restore();

Notez qu'ici la méthode query est simulée sur une instance, et n'a aucune implication sur le mécahnisme sous-jacent, donc seul le createConnection doit être restauré.

stubbing de la méthode .query sur le prototype de connexion

Cette technique est un peu plus délicate, car le pilote mysql n'expose pas officiellement sa connexion pour l'importation. (Eh bien, vous pouvez simplement importer uniquement le module implémentant la connexion, mais il n'y a aucune garantie que tout refactoring ne le déplacera pas à partir de là). Donc, pour obtenir une référence au prototype, je crée généralement une connexion et je traverse la chaîne constructeur-prototype:

Je le fais généralement en une seule ligne, mais je vais le décomposer en étapes et l'expliquer ici:

Pendant la configuration:

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

Pendant le démontage

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

Notez que nous ne nous moquons pas de la méthode createConnection ici. Toutes les validations des paramètres de connexion se produiront toujours (ce que je veux qu'elles se produisent. J'aspire à travailler avec un maximum de pièces authentiques - donc simulez le minimum absolu requis pour obtenir un test rapide). Cependant - le query est simulé sur le prototype et doit être restauré.

Notez également que si vous travaillez chirurgicalement, le verify sera sur la méthode fictive, pas sur le mockTarget.

Voici une bonne ressource à ce sujet: http://devdocs.io/sinon~6-stubs /

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top