Com es construeixen aplicacions de reacció estable mitjançant TDD i la biblioteca de proves de reaccions

Quan vaig començar a aprendre amb React, vaig tenir dificultats per provar les meves aplicacions web d’una manera útil i intuïtiva. Vaig fer servir Enzyme with Jest per fer que un component fos pla cada vegada que volia provar-lo.

Per descomptat, he abusat absolutament de la funció de prova d’instantànies.

Bé, almenys he escrit una prova, oi?

Potser heu sentit a algun lloc que la unitat d’escriptura i les proves d’integració milloren la qualitat del programari que escriviu. Les males proves, en canvi, condueixen a una falsa confiança.

Fa poc vaig assistir a un taller amb Kent C. Dodds a workshop.me on ens va ensenyar a escriure millors proves d’integració per a aplicacions React.

També ens va enganyar perquè utilitzéssim la seva nova biblioteca de proves per provar l'aplicació de la mateixa manera que ho faria un usuari.

En aquest article aprendrem a practicar TDD per crear aplicacions sòlides React creant un feed de comentaris. Per descomptat, aquest procés s’aplica a gairebé tot el desenvolupament de programari, no només a les aplicacions React o JavaScript.

Començar

En primer lloc, executem l’aplicació create-react i instal·lem les dependències. Suposo que, llegint un article de prova d’aplicacions, probablement ja esteu familiaritzat amb la instal·lació i l’inici de projectes JavaScript. Estic fent servir filats aquí en lloc de npm.

Creeu un feed de comentaris per a l'aplicació
Feed de comentaris de CD
filats

Tal com està, podem eliminar tots els fitxers del directori src, excepte index.js. A continuació, creeu una carpeta nova amb el nom "components" i una altra carpeta amb el nom "contenidors" directament a la carpeta "src".

Per fer proves d’utilitat, construiré aquesta aplicació amb la biblioteca de proves React de Kent. És una utilitat de proves lleugeres que demana al desenvolupador que provi la seva aplicació de la mateixa manera que s’utilitza.

Igual que Enzyme, exporta una funció de renderització, però aquesta funció de renderització sempre fa un desplegament complet del component. Exporta mètodes d’ajuda que podeu utilitzar per localitzar elements mitjançant etiquetes, text o identificadors de prova. Enzyme també ho fa amb la seva API de muntatge, però l'abstracció que crea ofereix més opcions, moltes de les quals us permeten provar els detalls de la implementació.

Ja no volem provar els detalls de la implementació. Volem representar un component i veure si passen les coses correctes quan fem clic o canviem alguna cosa a la interfície d'usuari. Això és! S'ha acabat la comprovació directa dels accessoris, l'estat o els noms de les classes.

Instal·lem-lo i comencem a treballar.

Yarn Add React Testing Library

Creeu un feed de comentaris amb TDD

Fem d’aquest primer component l’estil TDD. Comenceu el corredor de prova.

Prova del fil - rellotge

A la carpeta contenidor hi afegim un fitxer anomenat CommentFeed.js. Al costat, afegiu un fitxer anomenat CommentFeed.test.js. Per a la primera prova, assegureu-vos que els usuaris puguin crear comentaris. Massa aviat? D’acord, com que encara no tenim cap codi, comencem amb una prova més petita. A veure si podem representar el feed.

Algunes notes sobre la biblioteca de proves React

Primer de tot, tingueu en compte la funció de renderització. És similar a com React-Dom fa un component al DOM, però torna un objecte que podem desestructurar per obtenir ajudes útils per a proves. En aquest cas obtenim queryByText, que retorna aquest element HTML per a un text determinat que esperem al DOM.

Els documents de React Testing Library tenen una jerarquia que podeu utilitzar per decidir quin mètode de consulta o recuperació cal utilitzar. En general, l'ordre té aquest aspecte:

  • getByLabelText (entrades del formulari)
  • getByPlaceholderText (només si la vostra entrada no té cap etiqueta, menys accessible!)
  • getByText (botons i capçaleres)
  • getByAltText (imatges)
  • getByTestId (utilitzeu-ho per a coses com ara text dinàmic o elements estranys que vulgueu provar)

Cadascun d’aquests té una queryByFoo associada que fa el mateix, tret que la prova no fallarà si no es troba cap element. Utilitzeu això quan només proveu la presència d'un element.

Si cap d'aquests mètodes no fa exactament el que busqueu, el mètode de representació també retorna l'element DOM associat a la propietat del contenidor, de manera que podeu utilitzar-lo com a container.querySelector ("body #root").

El primer codi d’implementació

La implementació ara es veurà força senzilla. Només hem d’assegurar-nos que el feed de comentaris estigui al component.

Podria ser pitjor: vull dir, volia escriure tot aquest article mentre dissenyava components. Afortunadament, a les proves no els importen massa els estils, de manera que ens podem centrar en la lògica de la nostra aplicació.

La següent prova és per veure si podem fer comentaris. Tot i això, encara no tenim comentaris. Afegim també aquest component. Però després de la prova.

També crearé un objecte de suport per contenir les dades que podem reutilitzar en aquestes proves.

En aquest cas, comprovaré que el nombre de comentaris coincideix amb el nombre d’elements aprovats a CommentFeed. És trivial, però el fracàs de la prova ens dóna l'oportunitat de crear el fitxer Comment.js.

Aquest verd il·lumina la nostra suite de proves perquè puguem continuar sense por. Tothom saluda TDD, el nostre salvador. Funciona, per descomptat, si li donem una matriu buida. Però, i si li donem objectes reals?

Hem d’actualitzar la nostra implementació per renderitzar contingut realment. Prou fàcil ara que sabem cap a on anem, oi?

Mireu això, la nostra prova torna a tenir èxit. Aquí teniu una imatge preciosa de la seva bellesa.

Fixeu-vos que mai no he dit que haguéssim d'iniciar el nostre programa amb l'inici del fil? Ho seguirem així una estona. La qüestió és que haureu de sentir el codi amb la vostra ment.

L’estil és exactament el que hi ha a l’exterior: és el que importa a l’interior.

En cas que encara vulgueu iniciar l'aplicació, actualitzeu index.js de la següent manera:

Afegeix un comentari

Aquí es fa més divertit. Aquí, no només hem de buscar adormits nodes DOM, també hem de fer coses amb ells i comprovar el comportament. Totes les altres coses van ser un escalfament.

Comencem descrivint el que vull d’aquest formulari. Hauria:

  • conté l'entrada de text per a l'autor
  • Conté una entrada de text per al comentari
  • té un botó d'enviament
  • Finalment, truqueu a l'API o a qualsevol altre servei que crearà i desarà el comentari.

Podem escriure aquesta llista en una única prova d’integració. Per als casos de proves anteriors, vam considerar que era bastant lent, però ara anirem a agafar el ritme i intentarem clavar-lo d’un cop.

Noteu com s'està desenvolupant el nostre conjunt de proves? Hem passat de codificar els accessoris en els seus propis casos de prova a crear una fàbrica per a ells.

Organitzar, actuar, afirmar

Aquesta prova d’integració següent es pot desglossar en tres parts: Organitzar, Actuar i Aplicar.

  • Organitzeu: creeu accessoris i altres accessoris per al cas de prova
  • Act: Simula canvis en elements com ara l'entrada de text o els clics de tecla
  • Confirma: confirma que les funcions desitjades s’han anomenat el nombre correcte de vegades i amb els arguments correctes

Hi ha algunes suposicions sobre el codi, com ara el nom de les nostres etiquetes o el fet que tindrem un punt propi createComment.

Quan cerquem entrades, volem intentar trobar-les pel seu nom. Això prioritza l'accessibilitat a l'hora de crear les nostres aplicacions. La forma més senzilla d’obtenir el formulari és amb container.querySelector.

A continuació, hem d'assignar nous valors a les entrades i simular el canvi per actualitzar el seu estat. Aquest pas pot semblar una mica estrany, ja que normalment introduïm un caràcter alhora i actualitzem l'estat del component per a cada personatge nou.

Aquesta prova es comporta més com copiar / enganxar una cadena buida a "Sòcrates". En aquest moment no hi ha cap problema actual, però és possible que vulguem escriure-ho per si apareix més endavant.

Després d’enviar el formulari, podem fer afirmacions sobre quins accessoris es van cridar amb quins arguments. També podríem utilitzar aquest moment per comprovar si s’han suprimit les entrades del formulari.

És intimidatori? No necessito tenir por, fill meu, per aquest camí. En primer lloc, afegiu el formulari a la funció de representació.

Podria dividir aquest formulari en un component separat, però no ho faré per ara. En lloc d'això, l'afegiré a la meva "Llista de desitjos de Refactor" que conservo al costat del meu escriptori.

Aquesta és la manera de TDD. Si sembla que es pot reelaborar alguna cosa, anoteu-la i continueu. Refactoritzeu-vos només quan la presència d’una abstracció us sigui beneficiosa i no us sembli innecessari.

Recordeu quan vam redissenyar la nostra suite de proves creant la fàbrica createProps? Tal qual. També podem revisar proves.

Ara afegim els mètodes de classe handleChange i handleSubmit. S’activen quan canviem una entrada o enviem el nostre formulari. També inicialitzaré el nostre estat.

I ho va fer. Les nostres proves tenen èxit i tenim alguna cosa que s’assembla a una aplicació real. Com són els nostres informes?

No està malament. Si ignorem totes les configuracions a index.js, tenim una aplicació web completament coberta pel que fa a les línies executades.

Per descomptat, hi ha probablement altres casos que voldríem provar per assegurar-nos que l’aplicació funcioni tal com es volia. Aquest número de cobertura és només el que pot presumir el vostre cap quan parla amb les altres cohorts.

Desig de comentaris

Què tal si comprovem si ens agrada un comentari? Pot ser un bon moment per començar a establir un concepte d’autenticació a la nostra aplicació. Però encara no saltarem massa lluny. Actualitzem primer la nostra fàbrica d’atrezzo per afegir un camp d’autenticació juntament amb els identificadors dels comentaris que hem generat.

L'usuari que està "autenticat" rep la seva propietat d'autenticació passada per l'aplicació. Es noten totes les accions rellevants per a l'autenticació.

En moltes aplicacions, aquesta propietat pot contenir algun tipus de testimoni d’accés o galeta que s’envia quan es fan sol·licituds al servidor.

Al client, la presència d'aquesta propietat informa l'aplicació que l'usuari pot veure el seu perfil o altres rutes protegides.

Tot i això, en aquest exemple de prova, no ens endinsarem massa en l’autenticació. Imagineu-vos un cas com aquest: quan entreu a una sala de xat, proporcioneu el vostre nom de pantalla. A partir d’aquest moment, sou responsable de qualsevol comentari que faci servir aquest nom de visualització, fins i tot si algú altre ha iniciat la sessió amb aquest nom.

Una vegada més, aquesta no és una gran solució. Tot i això, només volem provar que el component CommentFeed es comporti com hauria de fer-ho. No ens importa com hagin iniciat la sessió els nostres usuaris.

Dit d’una altra manera, és possible que tinguem un component d’inici de sessió completament diferent que s’encarrega de l’autenticació d’un usuari concret i, a continuació, l’envia mitjançant foc i fúria per inferir la totpoderosa propietat d’autenticació que poden utilitzar per fer estralls a la nostra aplicació.

Fem "m'agrada" un comentari. Afegiu aquest següent cas de prova i actualitzeu la fàbrica d’elements amb likeComment.

I ara per a la implementació, actualitzem primer el component de comentari amb un botó "M'agrada" i un atribut "data-testid" perquè el puguem trobar.

Vaig posar l'identificador de prova just al botó perquè puguem simular immediatament un clic sense haver d'anidar els selectors de consultes. També he adjuntat un controlador onClick al botó perquè cridi a la funció onLike que li ha passat.

Ara afegim aquest mètode de classe al nostre CommentFeed:

És possible que us pregunteu per què no només passem l’accessori likeComment directament al component de comentari. Per què la convertim en una propietat de classe?

En aquest cas, no necessitem crear aquesta abstracció, ja que és força senzilla. En el futur, podem afegir més gestors onClick que puguin gestionar esdeveniments d’anàlisi, per exemple, o iniciar una subscripció a futurs comentaris sobre aquesta publicació.

La possibilitat d'agrupar diverses trucades de funcions diferents al mètode handleLike d'aquest component de contenidor té els seus avantatges. També podríem utilitzar aquest mètode per actualitzar l'estat del component després d'un "M'agrada" amb èxit si així ho decidim.

Aversió als comentaris

En aquest moment tenim proves de treball per representar, crear i agradar comentaris. Per descomptat, no hem implementat la lògica que realment fa això: no actualitzem la botiga ni escrivim a una base de dades.

També podeu trobar que la lògica que estem provant és fràgil i no és especialment adequada per a un feed de comentaris al món real. Per exemple, què passa si intentem agradar un comentari que ja ens ha agradat? Augmentarà el nombre d’agradats de manera indefinida o no els agradarà? Puc agradar els meus propis comentaris?

Deixo a la vostra imaginació ampliar la funcionalitat dels components, però un bon començament seria escriure un nou cas de prova. L'exemple següent es basa en el supòsit que volem rebutjar un comentari que ja ens ha agradat:

Tingueu en compte que puc veure els meus propis comentaris en aquest feed de comentaris que estem creant. Qui ho fa?

He actualitzat el component de comentari amb lògica per veure si a l'usuari actual li agradava o no el comentari.

Bé, he enganyat una mica: on anteriorment passàvem l’autor a la funció onLike, vaig canviar a currentUser, l’autenticitat que es va passar al component Comment.

Al cap i a la fi, no tindria sentit que l’autor del comentari aparegués quan a algú li agrada el comentari.

Em vaig adonar d'això perquè escrivia molt proves. Si només hagués programat per casualitat, potser hauria passat de llarg fins que un dels meus companys m’hagués denunciat la meva ignorància.

Però aquí no hi ha ignorància, només les proves i el següent codi. Assegureu-vos que el FeedFeed s’actualitzi per propagar la propietat auth. Amb els controladors onClick, podem ometre la transferència de la propietat auth, ja que podem derivar-la de la propietat auth als mètodes handleLike i handleDislike de l’element pare.

Embolicar

Esperem que el vostre conjunt de proves sembli un arbre de Nadal il·luminat.

Hi ha tantes rutes diferents que poden resultar una mica aclaparadores. Cada vegada que tingueu una idea d'alguna cosa, només cal que l'escriviu, ja sigui en paper o en un bloc de proves nou.

Suposem que realment voleu implementar handleLike i handleDislike en un mètode de classe única, però en aquest moment teniu prioritats diferents. Podeu fer-ho documentant el següent en un cas de prova:

Això no vol dir que hagueu d’escriure una prova completament nova. També podeu actualitzar els dos casos anteriors. Tanmateix, el punt és que podeu utilitzar el corredor de proves com una llista de tasques més important per a la vostra aplicació.

enllaços útils

Hi ha un gran contingut que cobreix les proves en general. Aquí hi ha alguns que van inspirar aquest article, així com les meves pròpies pràctiques.

  • "Presentació de la biblioteca de proves de reaccions" de Kent C. Dodds. És una bona idea comprendre la filosofia que hi ha darrere d’aquesta biblioteca de proves.
  • "Anti-patrons de proves de programari" de Kostis Kapelonis. Un article molt detallat que tracta les proves d’unitat i d’integració. A més, com no fer-les.
  • "Test Driven Development by Example" de Kent Beck. Aquest és un llibre físic que cobreix els patrons TDD. No és massa llarg i s’escriu en converses per millorar la digestibilitat.

Espero que això us mantingui ocupat durant un temps.

Tens curiositat per obtenir més publicacions o comentaris divertits? Si us ha agradat aquest article, feu-me uns quants clics i seguiu-me a Medium, Github i Twitter.