Aufbau eines Teams zur Abfrage von Daten aus der Datenbank - Teil 2

Im vorherigen Teil habe ich mich auf die Tatsache konzentriert, dass das Team, das ich entwickle, Verhaltensweisen implementiert, die mit diesem Test beschrieben werden können:


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

Es scheint mir jetzt, dass es nicht ganz das ist, was ich möchte, wenn ich Promise als Ergebnis Promise und es verarbeite. Es wäre besser, wenn das Team selbst diese Routinearbeit ausführen würde und das Ergebnis beispielsweise im Redux Repository abgelegt würde. Ich werde versuchen, den vorhandenen Test neu zu schreiben, um meine neuen Erwartungen darin auszudrücken:


 const store = require('../../src/store'); const DbMock = require('../mocks/DbMock'); const db = new DbMock(); const Request = require('../../src/storage/Request'); const options = { tableName: 'users', query: { Id: 1 } }; let request = null; beforeEach(() => { request = new Request(db, store); }); it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); }); 

Dies ist wahrscheinlich bequemer, obwohl ich jetzt die execute der Request Klasse unterrichten muss, um die Rückrufmethode auszuführen, wenn der Benutzer sie als Argument übergibt. Sie können nicht darauf verzichten, da ich bei der execute vermutlich asynchrone Aufrufe verwende, deren Testergebnisse nur getestet werden können, wenn sie davon überzeugt sind, dass ihre Ausführung abgeschlossen ist.


Weiter ... Wenn ich mir die erste Codezeile anschaue, muss ich, bevor ich zum Bearbeiten des Codes der Request Klasse zurückkehren kann, das Redux Paket zum Projekt hinzufügen, mindestens einen implementieren und diesen Reduzierer separat im Store verpacken. Der erste Test wird wahrscheinlich für das Getriebe sein:


 const reduce = require('../../src/reducers/user'); it('should return new state', () => { const user = { Id: 1, Name: 'Jack'}; const state = reduce(null, {type: 'USER', user }); expect(state).toEqual(user); }); 

Ich führe die Tests durch und stimme Jasmine dass zusätzlich zu allen vorherigen Fehlern ein Modul mit dem Namen ../../src/reducers/user nicht gefunden wurde. Deshalb werde ich es schreiben, zumal es verspricht, winzig und schrecklich vorhersehbar zu sein:


 const user = (state = null, action) => { switch (action.type) { case 'USER': return action.user; default: return state; } }; module.exports = user; 

Ich führe Tests durch und sehe keine radikalen Verbesserungen. Dies liegt daran, dass ich das Modul ../../src/store , dessen Existenz ich im Test für meine Request Klasse angenommen habe, noch nicht implementiert habe. Und es gibt auch noch keinen Test für ihn. Ich werde natürlich mit dem Test beginnen:


 describe('store', () => { const store = require('../src/store'); it('should reduce USER', () => { const user = { Id: 1, Name: 'Jack' }; store.dispatch({type: 'USER', user }); const state = store.getState(); expect(state.user).toEqual(user); }); }); 

Tests? Es gibt weitere Berichte über das Fehlen des store , daher werde ich mich sofort darum kümmern.


 const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('./reducers/user'); const reducers = combineReducers({user}); const store = createStore(reducers); module.exports = store; 

Da ich weiß, dass ich mehr als ein Getriebe haben werde, bin ich bei der Implementierung des ein wenig voraus und verwende beim combineReducers Methode combineReducers . Ich führe die Tests erneut aus und sehe eine neue Fehlermeldung, die mich darüber execute , dass die execute meiner Request Klasse nicht wie in meinem Test vorgeschlagen funktioniert. Aufgrund der Ausführungsmethode wird der Benutzerdatensatz nicht im . Es ist Zeit, die Request Klasse Request .


Ich erinnere mich, wie jetzt der Test der execute aussieht:


 it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); }); 

Und ich werde den Code der Methode selbst korrigieren, damit der Test ausgeführt werden kann:


 execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.store.dispatch({ type: 'USER', user: item }); if(typeof callback === 'function') callback(); }); } 

Ich tippe die npm test und ... Bingo! Meine Anfrage lernte nicht nur, Daten aus der Datenbank zu empfangen, sondern sie auch im zukünftigen Verarbeitungsprozesses zu speichern, damit nachfolgende Vorgänge diese Daten problemlos empfangen können.


Aber! Mein Handler kann nur eine Art von Aktion in den , was seine Funktionen stark einschränkt. Ich möchte diesen Code wiederholt verwenden, wenn ich einen Datensatz aus der Datenbank extrahieren und zur weiteren Verarbeitung unter dem von mir benötigten Schlüssel an die Statuscontainerzelle senden muss. Und so fange ich wieder an, den Test umzugestalten:


 const options = { tableName: 'users', query: { Id : 1 }, success (result, store) { const type = 'USER'; const action = { type , user: result }; store.dispatch(action); } }; it('should dispatch action if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); }); 

Mir kam der Gedanke, dass es schön wäre, die Request Klasse vor ungewöhnlichen Funktionen für die Verarbeitung von Abfrageergebnissen zu schützen. Semantisch Request ist eine Anfrage. Erfüllte die Anfrage, erhielt die Antwort, die Aufgabe ist erledigt, der Grundsatz der alleinigen Verantwortung der Klasse wird respektiert. Und lassen Sie jemanden, der speziell in diesem Prozess geschult ist, mit den Ergebnissen umgehen, dessen alleinige Verantwortung eine bestimmte Version der Verarbeitung selbst sein soll. Aus diesem Grund habe ich beschlossen, die success auf die Anforderungseinstellungen zu übertragen. Dies ist die Aufgabe, die von der Anforderung erfolgreich zurückgegebenen Daten zu verarbeiten.


Tests, jetzt können Sie nicht ausführen. Ich verstehe das intellektuell. Ich habe nichts im Test selbst behoben und nichts an der Implementierung geändert, und die Tests sollten weiterhin erfolgreich ausgeführt werden. Aber emotional muss ich den Befehl npm test ausführen und ihn execute store.dispatch(...) bearbeite ich die Implementierung meiner Methode store.dispatch(...) in der Request Klasse, um die Zeile durch den Aufruf von store.dispatch(...) durch die Zeile mit dem Aufruf von this.options.success(...) zu this.options.success(...) :


 execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.options.success(item, this.store); if(typeof callback !== 'undefined') callback(); }); } 

Ich führe Tests durch. Voila! Tests sind komplett grün. Das Leben wird besser! Was weiter? Ich sehe sofort, dass Sie den Titel des Tests ändern müssen, da dies nicht ganz richtig ist. Der Test überprüft nicht, ob das Versenden der Methode als Ergebnis der Anforderung erfolgt, sondern ob die Abfrage den Status im Container aktualisiert. Deshalb ändere ich den Titel des Tests in ... nun, zum Beispiel:


 it('should update store user state if record exists', () => { request.configure(options); request.execute(() => { const user = store.getState().user; expect(user.Id).toEqual(options.query.Id); expect(user.Name).toEqual('Jack'); }); }); 

Was weiter? Und dann denke ich, es ist Zeit, nicht darauf zu achten, dass meine Anfrage anstelle der angeforderten Daten einen Fehler zurückgibt. Dies ist kein so unmögliches Szenario. Richtig? Und die Hauptsache ist, dass ich in diesem Fall nicht in der Lage bin, den erforderlichen Datensatz vorzubereiten und an meinen KYC-Operator zu senden, um die Integration zu ermöglichen, mit der ich den gesamten Code schreibe. Ist das so Also Zuerst schreibe ich einen Test:


 it('should add item to store error state', () => { options.query = { Id: 555 }; options.error = (error, store) => { const type = 'ERROR'; const action = { type, error }; store.dispatch(action); }; request.configure(options); request.execute(() => { const error = store.getState().error; expect(Array.isArray(error)).toBeTruthy(); expect(error.length).toEqual(1); expect(error[0].message).toEqual('Something goes wrong!'); }); }); 

Ich weiß nicht, ob die Struktur des Tests zeigt, dass ich Zeit und Geld gespart und ein Minimum an Code geschrieben habe, um zu überprüfen, ob die Anforderung einen Fehler zurückgibt. Nein gesehen?


Ich möchte keine Zeit damit verschwenden, zusätzliche Implementierungen von TableMock , die Fehler imitieren. Ich entschied, dass im Moment ein paar bedingte Konstrukte in der vorhandenen Implementierung für mich völlig ausreichen würden, und schlug vor, dass dies durch Abfrage- query angepasst query kann. Meine Annahmen sind also:


  • Wenn die options.query in der Abfrage options.query 1 ist , gibt meine options.query immer das zulässige Promise mit dem allerersten Datensatz aus der Sammlung zurück.
  • Wenn die options.query in der Abfrage options.query 555 lautet, gibt meine options.query immer ein abgelehntes Promise mit einer darin enthaltenen Error , deren message Etwas geht schief" lautet. .

Das ist natürlich alles andere als ideal. Es wäre für die Wahrnehmung viel lesbarer und bequemer, die entsprechenden DbMock Instanzen zu implementieren, beispielsweise HealthyDbMock , FaultyDbMock , EmptyDbMock . Aus den Namen, aus denen sofort hervorgeht, dass der erste immer korrekt funktioniert, der zweite immer falsch, und beim dritten können wir davon ausgehen, dass anstelle des Ergebnisses immer null . Nachdem ich meine ersten Annahmen auf die oben genannte Weise überprüft habe, dass es meiner Meinung nach ein Minimum an Zeit in Anspruch nimmt, werde ich möglicherweise zwei zusätzliche Instanzen von DbMock , die ungesundes Verhalten simulieren.


Ich führe Tests durch. Ich erhalte den erwarteten Fehler des Fehlens der Eigenschaft, die ich im benötige, und ... ich schreibe einen weiteren Test. Diesmal für ein Getriebe, das Aktionen vom Typ ERROR .


 describe('error', () => { const reduce = require('../../src/reducers/error'); it('should add error to state array', () => { const type = 'ERROR'; const error = new Error('Oooops!'); const state = reduce(undefined, { type, error }); expect(Array.isArray(state)).toBeTruthy(); expect(state.length).toEqual(1); expect(state.includes(error)).toBeTruthy(); }); }); 

Führen Sie die Tests erneut aus. Alles wird erwartet, ein weiterer wurde zu den vorhandenen Fehlern hinzugefügt. Ich erkenne das :


 const error = (state = [], action) => { switch (action.type) { case 'ERROR': return state.concat([action.error]); default: return state; } }; module.exports = error; 

Ich führe erneut Tests durch. Der neue Reduzierer funktioniert wie erwartet, aber ich muss immer noch sicherstellen, dass er eine Verbindung zum Repository herstellt und die Aktionen verarbeitet, für die er vorgesehen ist. Daher schreibe ich einen zusätzlichen Test für die vorhandene Speichertestsuite:


 it('should reduce error', () => { const type = 'ERROR'; const error = new Error('Oooops!'); store.dispatch({ type, error }); const state = store.getState(); expect(Array.isArray(state.error)).toBeTruthy(); expect(state.error.length).toEqual(1); expect(state.error.includes(error)).toBeTruthy(); }); 

Ich führe Tests durch. Alles wird erwartet. Eine Aktion vom Typ ERROR verarbeitet den vorhandenen Speicher nicht. Ändern des vorhandenen Repository-Initialisierungscodes:


 const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('./reducers/user'); const error = require('./reducers/error'); const reducers = combineReducers({ error, user }); const store = createStore(reducers); module.exports = store; 

Zum hundertsten Mal warf er ein Netz ... Sehr gut! Das Repository sammelt jetzt empfangene Fehlermeldungen in einer separaten Containereigenschaft.


Jetzt werde ich einige bedingte Konstrukte in die vorhandene TableMock Implementierung TableMock und ihr TableMock , einige Abfragen auf diese Weise zu starten und einen Fehler zurückzugeben. Der aktualisierte Code sieht folgendermaßen aus:


 class TableMock { constructor(array){ this.container = array; } where(query){ this.query = query; return this; } fetch(){ return new Promise((resolve, reject) => { if(this.query.Id === 1) return resolve(this.container[0]); if(this.query.Id === 555) return reject(new Error('Something goes wrong!')); }); } } module.exports = TableMock; 

Ich führe die Tests aus und erhalte eine Nachricht über die nicht behandelte Ablehnung von Promise in der execute-Methode der Request Klasse. Ich füge den fehlenden Code hinzu:


 execute(callback){ const table = this.db.Model.extend({ tableName: this.options.tableName }); table.where(this.options.query).fetch().then((item) => { this.options.success(item, this.store); if(typeof callback === 'function') callback(); }).catch((error) => { this.options.error(error, this.store); if(typeof callback === 'function') callback(); }); } 

Und ich führe die Tests erneut durch. Und ??? Es gibt tatsächlich keinen Test für die execute der Request Klasse, diesen:


 it('should add item to store error state', () => { options.query = { Id: 555 }; options.error = (error, store) => { const type = 'ERROR'; const action = { type, error }; store.dispatch(action); }; request.configure(options); request.execute(() => { const error = store.getState().error; expect(Array.isArray(error)).toBeTruthy(); expect(error.length).toEqual(1); expect(error[0].message).toEqual('Something goes wrong!'); }); }); 

Er hat erfolgreich abgeschlossen. Daher kann die Abfragefunktionalität in Bezug auf die Fehlerbehandlung als implementiert betrachtet werden. Ein weiterer Test fiel, der das Repository auf Fehlerbehandlung überprüft. Das Problem ist, dass mein Speicherimplementierungsmodul in allen Tests allen Verbrauchern dieselbe statische Speicherinstanz zurückgibt. In dieser Hinsicht besteht die Überprüfung der Anzahl von Fehlern in dem Behälter nicht notwendigerweise, da der Versand von Fehlern bereits in zwei Tests erfolgt. Da zum Zeitpunkt des Teststarts bereits ein Fehler im Container vorhanden ist und dort während des Teststarts ein weiterer Fehler hinzugefügt wird. Also hier ist dieser Code:


 const error = store.getState().error; expect(error.length).toEqual(1); 

Löst eine Ausnahme aus und meldet, dass der Ausdruck error.length tatsächlich 2 und nicht 1 ist. Dieses Problem werde ich jetzt einfach lösen, indem ich den Speicherinitialisierungscode direkt in den Speichertestinitialisierungscode übertrage:


 describe('store', () => { const createStore = require('redux').createStore; const combineReducers = require('redux').combineReducers; const user = require('../src/reducers/user'); const error = require('../src/reducers/error'); const reducers = combineReducers({ error, user }); const store = createStore(reducers); it('should reduce USER', () => { const user = { Id: 1, Name: 'Jack' }; store.dispatch({type: 'USER', user }); const state = store.getState(); expect(state.user).toEqual(user); }); it('should reduce error', () => { const type = 'ERROR'; const error = new Error('Oooops!'); store.dispatch({ type, error }); const state = store.getState(); expect(Array.isArray(state.error)).toBeTruthy(); expect(state.error.length).toEqual(1); expect(state.error.includes(error)).toBeTruthy(); }); }); 

Der Initialisierungscode für den Test sieht jetzt etwas geschwollen aus, aber ich kann später zu seinem Refactoring zurückkehren.


Ich führe Tests durch. Voila! Alle Tests wurden abgeschlossen und Sie können eine Pause machen.

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


All Articles