📜 ⬆️ ⬇️

Developing a team to request data from the database - part 4, completing

This is a continuation of the story that began here and continued here and here .


In the last part, I wrote an integration test that demonstrates the process of initializing and executing a complete set of handlers that retrieve data from the database. But since it may take too long for writing not only the handler, but also the setup rules for all necessary requests to the database, from writing this test, today I decided to implement its modular version, designed to configure single handler. This test looks like this:


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

I run the tests and get a message that the module with the rules was not found. Once again, what will the handler need to extract from the rules?


  1. The name of the property key of the state container to which the record obtained from the database will be bound
  2. The name of the database table from which you want to extract the record
  3. The method that forms the request to be sent to the database in order to receive a response
  4. The dispatch method that sends the record received from the database to the state container storage.
  5. A dispatch method that sends an error, if one occurs, to the status container repository. I decided to first consolidate the errors in the state container, and then deal with them.

The set of rules seems to me to be a dictionary ( Map ), in which the key will be the name of the property of the state container and I will formulate the first unit test:


 describe('rules', () => { const rules = require('../src/rules'); it('should contain user rules', () => { const rule = rules.get('user'); expect(rule.table).toEqual('users'); }); }); 

I run the tests and Jasmine informs me that I now have two non-performing tests. In order not to complicate the task, I will start with the simplest rule that tells me that in order to assign a value to the user key of the status container, my query should be accessed for data in the users table. It seems to be all logical. I will write as it seems to me a little code.


 const makeRules = () => { const rules = new Map(); rules.set('user', { table: 'users' }); return rules; }; module.exports = makeRules(); 

I run tests, and I see that only the test for my processor drops, although the error is different now. However, I will return to this test later when I have at least one complete rule in my dictionary.


I will modify the test of the rules dictionary a little. Add the code that checks for the presence of the dispatcher method that handles the error of the query to the database:


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

I perform tests, make sure that I have two faulty tests again, and come back to refining the code of the factory method for generating the rules dictionary. I add a function to the literal object of the first rule:


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

Run tests again. The new rule fragment successfully passes the test, so I decide to add checks for all remaining rules:


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

I run the tests. The test suite for the rules dictionary again gives an error. I am writing 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(); 

I run the tests. The test of the rule set is again successfully executed and it seems to me that I can now take up writing code for the new version of the data request from the database itself. This time I will not use the class syntax, because I do not see any advantage from its use. There will be a lot of code at once, because I will ruthlessly copy the tested part of it from the existing request implementation, adding it to the check, in case the record from the database has already been retrieved and placed in the state container. So the 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; 

Running tests ... drum roll! And I see a line of green dots. All tests completed successfully. And so I’ll add another test to the suite that checks the correctness of error handling before I forget that in order for my pseudo- DbMock return an error, you need to ask her for an entry with Id equal to 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(); } 

I run the tests again. Everything works as expected. We can assume that the correct prototype of the query command to the database is ready and go back to refactoring and developing rules for configuring queries for it is already clear that the code for generating a set of rules will no longer be readable after adding just a couple more rules in the same format.



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