Open julien-topcu opened 2 years ago
pl.id == 'create'
(https://github.com/openwhyd/openwhyd/blob/master/test/integration/post.api.tests.js#L47) => cas à traiter par adaptateur, pour pas que create
soit transmis au domaineMesure / Visualisation du coverage sur la feature à refacto:
Analyse du coverage, pour mettre en évidence les cas fonctionnels non couvert
Analyse des tests de plus haut niveau pour voir si des usecases serait plus explicite à ce niveau là
Remboursement de la couverture fonctionnelle:
Inversion de controle entre la persistence et le controller /Rendre explicite le DTO coté base de donnée
Introduction de la couche domaine:
Constat -> Modele métier actuel est anémique:
Introduction des tests de critére d'acceptance en isolation (TDD avec l'étape précèdent)
Introduction des tests d'intégration vis a vis de notre persistence
docker-compose up -d
open http://localhost:8080
test/integration/post.api.tests.js
, puis finir d'écrire le test => https://gitlab.com/shodo.io/public/hexagonal-architecture-nodejs-migration/-/commit/08bcc1ac83e37cefc54efe1227e4f2d6e60e5689dans un premier terminal:
. ./env-vars-testing.sh
export MONGODB_PORT=27117
export MONGODB_DATABASE=openwhyd_test
npm start
dans un second terminal:
. ./env-vars-testing.sh
export MONGODB_PORT=27117
export MONGODB_DATABASE=openwhyd_test
npm run test-reset && npm run test:integration
test/integration/post.api.tests.js
(cf TODOs dans ce fichier)test/integration/post.api.tests.js
Montrer lors de la conf l'intérêt de faire des tests FIRST !
Repartir du test should re-add a track into a new playlist
et montrer que son introduction fait péter une demi-douzaine d'autres tests car ils sont liés entre eux.
Montrer l'évolution de la couverture du code liée aux fonctionnalités autour du concept de post, après le remboursement de la dette (test de controller post)
docker-compose up -d mongo # starts mongo on port 27117
source ./env-vars-testing-local.sh
npm run test:integration:legacy-post:cov
source ./env-vars-testing-local.sh
npm run test:integration:post:cov
Les 2 commandes produisent un rapport qui peuvent être comparé
Pour s'assurer d'être iso par rapport à la persistence d'un post, lancer les tests avec une approche approval testing basé sur des dumps de la db
$ docker-compose up -d mongo
$ npm run -s test:approval:coverage
(les tests approval lancent le serveur et nettoient la DB programmatiquement)
Pour visualiser directement le coverage dans vscode:
src
? (decision que c'est du dead code mais snapshoté dans les tests approval)img
createPlaylist
, pour rendre plus lisibles la séquentialité et le retour de fonction => https://github.com/openwhyd/openwhyd-solo/pull/13/files#diff-db419e2f985ae41f450c2e0c2589debe9b177a41d6fc3c26f23c648adf4e1c5eR114Jordan nous présente la ré-application de ce qu'on avait montré en live-stream:
reproduire rapidement ce qui s'est passé pendant le live => aller dans le mur, conclusion: need tests, moar coverage
ajouter des tests => surprise: découverte de side effects entre tests (violation de FIRST) => besoin de structure sûre pour nos prochains tests
grace au coverage, on se rend compte qu'il y a pas mal de use cases couverts par fonction, et pas mal de lignes non couvertes
npm run test:post:coverage
sur codacy createPlaylist
n'est pas couverte (cf https://app.codacy.com/gh/openwhyd/openwhyd/file/68022651853/coverage?bid=17325385&fileBranchId=17325385) => écriture d'un test integration (à montrer en live)on choisit de commencer par injecter createPlaylist
(sous forme de domaine service) car il est deja relativement bien isolé, mais aussi pour avancer vers le retrait du poison pill pl.id=="create"
soit on va vers typescript, soit on continue d'extraire le domaine.
createPlaylist
=> étranglement progressive du legacy, de gauche (APIs) à droite (mongodb)fetchUserById()
de UserCollection.insertPlaylist()
(cf code commenté)models/users
en utilisant le driver mongo directement dans getUserById
Objectif: finir déroullé ? (cf https://github.com/openwhyd/openwhyd-solo/pull/19)
[x] 6. Extraire la logique métier de la couche de persistence
[x] 7. Injection de dépendance
[ ] 9 8. Décommisionnement des Approval tests
[ ] corriger le bug d'incrementation d'id
[ ] TODO: Réintégrer dans la branche migration les infrastructures permettant de simplifier le lancement les tests et le coverage
Répétition pendant SHODAY.
=> branche: https://github.com/openwhyd/openwhyd-solo/tree/migration-2022-04-13-shoday
migration-start-with-approval-tests
ce3d2f4662b62693675c8866d0c3372fa166e424
(à cherry-pick pendant le talk)Je viens de créer deux snippets avec l'extension Snippet - Visual Studio Marketplace:
=> branche: migration-2022-04-15
Pour référence, voici:
Update suite à la répète de Lundi:
migration-start
pour qu'il pointe vers l'unique commit de https://github.com/openwhyd/openwhyd-solo/pull/21 => les tests d'intégration de post api n'ont plus besoin qu'on lance le serveur d'openwhydmigration-start
--> migration-start-devoxx-2022
, au cas oùnpm start
et kill
, en accord avec ce changementNote: je n'ai pas modifié le tag checkout migration-start-with-approval-tests
car il me semble qu'on ne lance plus ces tests d'intégration après la migration. => on aura probablement envie de le faire, si jamais on trouve un moyen simple et fluide d'exécuter les nouveaux tests d'intégration (requêtes db via SPI) après la migration.
Notes de 2ème répète:
insert
, y compris la partie sur les playlistscreatePlaylist
par le fait que ça ne dépend de rien d'autre => scope réduitplaylistRequest
(cf diff ci-dessous)diff --git a/app/controllers/api/post.js b/app/controllers/api/post.js
index 3728376..e32f36d 100644
--- a/app/controllers/api/post.js
+++ b/app/controllers/api/post.js
@@ -63,6 +63,7 @@ exports.actions = {
uId: httpParams.uId,
uNm: httpParams.uNm,
text: httpParams.text || '',
+ pl: extractPlaylistRequest(httpParams),
// fields that will be ignored by rePost():
name: httpParams.name,
eId: httpParams.eId,
@@ -105,23 +106,18 @@ exports.actions = {
}
}
- const playlistRequest = extractPlaylistRequest(httpParams);
-
- if (needToCreatePlaylist(playlistRequest)) {
+ if (needToCreatePlaylist(postQuery.pl)) {
const playlist = await features.createPlaylist(
httpParams.uId,
- playlistRequest.name
+ postQuery.pl.name
);
if (playlist) {
- postQuery.pl = { id: playlist.id, name: playlistRequest.name };
+ postQuery.pl.id = playlist.id;
// console.log('playlist was created', q.pl);
}
} else {
- postQuery.pl = {
- id: parseInt(playlistRequest.id),
- name: playlistRequest.name,
- };
- if (isNaN(playlistRequest.id)) delete postQuery.pl; //q.pl = null;
+ postQuery.pl.id = parseInt(postQuery.pl.id);
+ if (isNaN(postQuery.pl.id)) delete postQuery.pl; //q.pl = null;
}
actualInsert();
Déroulé du talk
0. Prérequis
Liens de référence:
Étapes de préparation pré-live-coding:
Mettre VSCode en thème "fond clair".
Installer Snippet - Visual Studio Marketplace pour VSCode.
Créer snippet "
basic functional test
" avec Snippet - Visual Studio Marketplace:contenu du snippet
```js // @ts-check // run with: $ npx mocha test/functional/createPlaylist.functional.tests.js const assert = require('assert'); const { createPlaylist } = require('../../app/domain/features'); describe('playlist', () => { it('should be created by a user without playlist', async () => { const playlistName = 'summer mega mix 2022'; const playlist = await createPlaylist('userWithoutPlaylist', playlistName); assert.equal(playlist.id, 0); assert.equal(playlist.name, playlistName); }); it('should be created by a user with playlist', async () => { const playlistName = 'summer mega mix 2023'; const playlist = await createPlaylist('userWithPlaylist', playlistName); assert.equal(playlist.id, 1); assert.equal(playlist.name, playlistName); }); }); ```Préparer l'environnement de live coding, dans le terminal de VSCode:
1. Faire une démo d'OpenWhyd
Adrien
app.route
, notamment lesapi
-->subdir
-->controllers/api/post.js
--> fonction intéressante: insertion d'un morceau de musique. (post Controller sur la méthodeinsert
)Objectifs:
Donc => Migration in situ
2. Préparation du terrain opératoire: Biopsies Clean Codiennes
But : Y voir plus claire sur la code base But caché (intention) : Montrer la fragilité de la code base
insert
decontrollers/api/post
controllers/api/post
,p
puisuId
uNm
de l'objetq
dansinsert
. Et expliquer qu'on est en train de changer la structure en base de données.(redémarrer serveur puis)Réexécuter les tests d'intégration et montrer que les messages d'erreurs ne permettent pas de voir ce qu'on a cassé3. Ajout des approval tests
When posting a track
approval.tests.js
et expliquer le test correspondant4. Amener plus de lisibilité dans le code
Lisibilité
Adrien:
insert
jusqu'àprocess playlist
en expliquant le codeinsert
a beaucoup de responsabilité => décider que la partiecreatePlaylist
est déjà un scope suffisant à refactorerJordan:
Extraction deextractPlaylistRequest
postRequest.pl = extractPlaylistRequest(...)
et dire que ça pourrait être fait directement à l'initialisation depostQuery
.undefined
parextractPlaylistRequest(...)
needToCreatePlaylist
dans le ifcreatePlaylist
plus explicite ?Adrien:
createPlaylist()
actualInsert()
superfluDiagnostic
createPlaylist
?userModel.createPlaylist()
5. Modéliser le domaine
domain
api
. Le domaine doit dépendre que des objets du domaine, mais il n'y pas de typage fort en JS.CreatePlaylist
dansapi.ts
Playlist
danstypes.ts
Promise(createPlaylist)
pour bien détourer la déclaration de la fonctioncreatePlaylist
CreatePlaylist
via JSDoc puis activer la vérification avects-check
playlist
et les warnings de "compilation" si on fait unplaylist.toto
createPlaylist
dansfeatures.js
dans le domaine.ts-check
dansfeatures
6. Extraire la logique métier de la couche de persistence
userModel.createPlaylist()
pour caractériser le métier qu'on veut migrer.test/functional/playlist.functional.test.js
) pour figer les comportements que l'on a compris et expliquer qu'on ne s'appuie pas sur les Approvals, car le domaine est testé en isolation pure.basic functional test
(clic droit + "insert snippet") et expliquer les testsuserModel
dansfeatures.createPlaylist()
en la commentantuserModel.createPlaylist()
et écrire un "algo" dansfeatures
spi.ts
UserRepository
.getUserById
qui retourne une Promise de Usertypes.ts
features a maintenant besoin d'une instance de UserRepository. Comme on ne veut pas se coupler avec le repository, on crée une factory
createFeatures` pour d'injecter une instance.userRepository
de la factoryuserRepository.getUserbyId
danscreatePlaylist
userModel
et expliquer la logique de création d'un id de playlist, la copier-coller dans features et la nettoyer.insertPlaylist
dans la SPI dans leUserRepository
features
createFeatures
userRepository
et la typer avec la SPI. Générer le squelette au moyen de l'IDE.$ npx mocha test/functional/playlist.functional.test.js
)7. Recabler le domaine avec la persistance
infrastructure
avecUserCollection.js
UserCollection
avec la SPI et générer le squeletteUserCollection
UserCollection.getUserById()
UserDocument
dansinfrastructure/type.ts
, typer le retour de la requête mongo dansgetUserbyId
et tenter de retourner leuserDocument
.mapToUser
et expliquer le rôle de l'anti-corruption layerinsertPlaylist
Jordan reprend la main du copilote.
fetch
et lesave
dansuserModel
Si le cherry-pick ne passe pas, vider l'index de git
8. Découplage du controller
post
en expliquant qu'on sort du fichier les instanciations. Commenter les require defeatures
etuserCollection
features
à la stack d'appel deinsert
jusqu'àApplication.js
subdir.js
en montrantapp.route
attachLegacyRoutesFromFile
9. Décommisionnement des Approval tests (optionnel)
Aller dans les tests d'intégration de post et de l'infra mongo
TODO:
[ ] Voir comment définir les types en JSDoc de manière globale pour éviter les imports