Il s'agit d'une continuation de l'histoire qui a commencé ici et s'est poursuivie ici et ici .
Dans la dernière partie, j'ai écrit un test d'intégration démontrant le processus d'initialisation et d'exécution d'un ensemble complet de gestionnaires qui extraient des données d'une base de données. Mais comme l'écriture de ce test peut prendre trop de temps pour l'exécuter, nécessaire pour coder non seulement le gestionnaire, mais aussi les règles de configuration pour toutes les requêtes nécessaires dans la base de données, j'ai décidé aujourd'hui d'implémenter sa version modulaire, conçue pour configurer et exécuter tout un gestionnaire. Ce test ressemble à ceci:
describe('requestHandler', () => { const createStore = require('redux').createStore; const reducers = require('../../src/reducers.js'); const DbMock = require('../mocks/DbMock'); const db = new DbMock(); const rules = require('../../src/rules'); const dbRequest = require('../../src/storage/dbRequest'); let request = null, store = null, context = null; beforeEach(() => { store = createStore(reducers); context = { db, store, rules }; request = dbRequest.bind(context, [ 'user' ]); expect(store.getState().user).toBeNull(); expect(store.getState().error).toEqual([]); }); it('should get user from database', (done) => { const assert = checkUser.bind(context, [ done ]); store.subscribe(assert); store.dispatch({type: 'NOTE', note: { Id: 1, UserRecordId: 1 }}); request(); }); function checkUser(args) { const state = store.getState(); if(state.user === null) return; const user = state.user; expect(user.Id).toEqual(1); expect(user.Name).toEqual('Jack'); const checkIsCompleted = args[0]; checkIsCompleted(); } });
J'exécute les tests et reçois un message indiquant que le module contenant les règles est introuvable. Encore une fois, que devra retirer le gestionnaire des règles?
- Nom de la clé de propriété du conteneur de propriétés à laquelle l'enregistrement reçu de la base de données sera lié
- Le nom de la table de base de données à partir de laquelle vous souhaitez extraire l'enregistrement
- La méthode qui forme la demande qui devra être envoyée à la base de données pour obtenir une réponse
- La méthode de répartiteur qui envoie l'enregistrement reçu de la base de données au magasin de conteneurs d'état.
- Une méthode de répartition qui envoie une erreur, le cas échéant, au stockage du conteneur d'état. J'ai décidé de consolider d'abord les erreurs dans le conteneur d'état, puis de les traiter.
L'ensemble de règles me semble être un dictionnaire ( Map
), dans lequel la clé sera le nom de la propriété du conteneur d'état et je formulerai probablement le premier test unitaire:
describe('rules', () => { const rules = require('../src/rules'); it('should contain user rules', () => { const rule = rules.get('user'); expect(rule.table).toEqual('users'); }); });
Je lance les tests et Jasmine
me dit que maintenant j'ai deux tests qui ont échoué. Afin de ne pas compliquer la tâche, je vais commencer par une règle simple qui me dit que pour attribuer une valeur à la clé user
du conteneur d'état, ma requête doit aller dans la table des users
pour les données. Tout semble logique. Je vais écrire comme il me semble un petit code.
const makeRules = () => { const rules = new Map(); rules.set('user', { table: 'users' }); return rules; }; module.exports = makeRules();
J'exécute les tests et je constate que seul le test de mon gestionnaire se bloque, bien que l'erreur soit maintenant différente. Cependant, je reviendrai à ce test plus tard, lorsque j'aurai au moins une règle complète dans mon dictionnaire.
Je vais modifier un peu le test du dictionnaire de règles. J'ajouterai un code qui vérifie la présence d'une méthode de répartiteur qui gère une erreur lors de l'exécution d'une requête dans la base de données:
describe('rules', () => { const rules = require('../src/rules'); it('should contain user rules', () => { const rule = rules.get('user'); expect(rule.table).toEqual('users'); expect(rule.onError.name).toEqual('dispatchError'); expect(typeof rule.onError).toEqual('function'); }); });
J'exécute les tests, m'assure que j'ai à nouveau deux tests échoués et reviens à la finalisation du code de méthode d'usine pour générer le dictionnaire de règles. J'ajoute la fonction au littéral objet de la première règle:
const makeRules = () => { const rules = new Map(); rules.set('user', { table: 'users', onError: function dispatchError(error, store) { const action = { type: 'ERROR', error }; store.dispatch(action); } }); return rules; }; module.exports = makeRules();
Relancer les tests. Le nouveau fragment de règle réussit le test, je décide donc d'ajouter des vérifications pour toutes les règles restantes:
describe('rules', () => { const rules = require('../src/rules'); it('should contain user rules', () => { const rule = rules.get('user'); expect(rule.table).toEqual('users'); expect(rule.onError.name).toEqual('dispatchError'); expect(typeof rule.onError).toEqual('function'); expect(rule.onSuccess.name).toEqual('dispatchUser'); expect(typeof rule.onSuccess).toEqual('function'); expect(rule.query.name).toEqual('getUserQuery'); expect(typeof rule.query).toEqual('function'); }); });
Je lance des tests. L'ensemble de tests pour le dictionnaire de règles renvoie à nouveau une erreur. J'écris le code:
const makeRules = () => { const rules = new Map(); rules.set('user', { table: 'users', onError: function dispatchError(error, store) { const action = { type: 'ERROR', error }; store.dispatch(action); }, onSuccess: function dispatchUser(user, store) { const action = { type: 'USER', user }; store.dispatch(action); }, query: function getUserQuery(store) { const state = store.getState(); if(state.note === null) return null; return { Id: state.note.UserRecordId }; } }); return rules; }; module.exports = makeRules();
Je lance des tests. Le test de l'ensemble de règles est à nouveau effectué avec succès et il me semble que je peux maintenant commencer à écrire du code pour la nouvelle version de la demande de données de la base de données. Cette fois, je n'utiliserai pas la syntaxe de classe, car je ne vois aucun avantage à son utilisation. Il y aura beaucoup de code à la fois, car je copie impitoyablement la partie testée de l'implémentation de demande existante, en la complétant par une vérification, au cas où l'enregistrement de la base de données a déjà été extrait et placé dans le conteneur d'état. Donc le code:
function dbRequest(args){ const key = args[0]; const getQuery = this.rules.get(key).query; const dispatchUser = this.rules.get(key).onSuccess; const dispatchError = this.rules.get(key).onError; const tableName = this.rules.get(key).table; const table = this.db.Model.extend({ tableName: tableName }); const state = this.store.getState(); if(state[key] !== null) return; const query = getQuery(this.store); if(query === null) return; table.where(query).fetch().then((item) => { dispatchUser(item, this.store); }).catch((error) => { dispatchError(error, this.store); }); } module.exports = dbRequest;
Tests en cours ... roulement de tambour! Et je vois une ligne de points verts. Tous les tests ont réussi. Et donc je vais ajouter un autre test à l'ensemble qui vérifie l'exactitude de la gestion des erreurs, jusqu'à ce que DbMock
que pour que ma pseudo- DbMock
renvoie une erreur, je dois lui demander une entrée avec un Id
égal à 555 :
it('should add error in store state', (done) => { const assert = checkErrorHasBeenAdded.bind(context, [ done ]); store.subscribe(assert); store.dispatch({type: 'NOTE', note: { Id: 1, UserRecordId: 555 }}); request(); }); function checkErrorHasBeenAdded(args){ const state = store.getState(); if(state.error.length === 0) return; const error = state.error; expect(Array.isArray(error)).toBeTruthy(); expect(error.length).toEqual(1); expect(error[0].message).toEqual('Something goes wrong!'); const checkIsCompleted = args[0]; checkIsCompleted(); }
Je relance les tests. Tout fonctionne comme prévu. Nous pouvons supposer que le prototype correct de la commande de requête dans la base de données est prêt et revenir à la refactorisation et au développement des règles de configuration des requêtes, car il est désormais clair que le code pour générer le jeu de règles cessera d'être lisible après avoir ajouté quelques règles de plus dans le même format.