Un archiviatore di email ha esattamente un modo di fallire che conta davvero: fare alla tua posta qualcosa che non hai chiesto. Segnare un messaggio come letto, archiviarlo nella cartella sbagliata, cancellare la cosa sbagliata. Per questo ogni funzionalità di MailVault viene rilasciata nello stesso modo: solo dopo che una macchina ha aperto l'applicazione reale, ha eseguito la funzionalità come farebbe una persona, e ha letto il risultato direttamente dallo schermo.
Questa è una nota sui progressi di come funziona il tutto, perché l'impianto si è rivelato più interessante delle funzionalità stesse.
I test usano l'app. Non la simulano.
MailVault è un'applicazione Tauri: un backend Rust che si occupa del lavoro IMAP, un'interfaccia React in una webview nativa. Tauri ha qui un superpotere poco sfruttato: compilare l'app con la sua funzionalità webdriver e l'applicazione reale diventa controllabile da remoto, allo stesso modo dei browser. La nostra suite gira su WebdriverIO con la capacità wry attraverso tauri-driver, contro il binario effettivamente compilato.
La distinzione conta. Un test di componente esegue il rendering di una lista in un browser immaginario e verifica un output virtuale. I nostri test end-to-end aprono la stessa finestra che vede un utente, aspettano che le stesse righe vengano disegnate, cliccano gli stessi pulsanti, e poi controllano cosa dicono davvero, dopo, l'interfaccia e la posta sul server. Un estratto da un'esecuzione di questa settimana, che testa la barra delle azioni sulla selezione:
Selection Action Bar effects
✓ marks a selected row as read and repaints it
✓ marks a row back as unread
✓ marks several selected rows as read in one action
✓ archives a selected row and flips its source icon to local
✓ unarchives it again
✓ moves a selected row to another folder and drops it from the list
✓ deletes a selected row from the server and drops it from the list
7 passing (833ms)
Ogni riga è un viaggio di andata e ritorno completo: un clic nell'interfaccia, una chiamata al backend Rust, una conversazione IMAP con un server, e un ridisegno che il test verifica con i propri occhi. Quando la suite dice che archiviare fa passare l'icona della fonte a locale, significa che una macchina ha osservato quel cambiamento.
I server di posta nascono e muoiono con l'esecuzione
Niente di tutto questo tocca un account reale. Ogni esecuzione avvia server IMAP fittizi su porte locali e li popola con due account noti, così la suite può esercitare il cambio di account e la casella unificata invece di un solo percorso ideale. Un account contiene deliberatamente settecento messaggi, più grande di entrambe le finestre di caricamento dell'app, così c'è sempre una casella genuinamente caricata a metà, lo stato in cui vivono i bug di paginazione.
L'app stessa è puntata verso una home directory usa e getta creata per l'esecuzione. Il suo archivio, le sue impostazioni, il suo database locale finiscono tutti in una cartella che un minuto prima non esisteva e che verrà cancellata alla fine dell'esecuzione. Nessuna credenziale, nessuna rete, nessuna posta reale nei paraggi del processo.
Il determinismo è l'altro motivo. I server di posta reali ti razionano. Gmail limita i download IMAP a 2.500 MB al giorno, di cui abbiamo scritto in la precedente Nota sul campo, e ogni provider è capriccioso in modi che una suite di test non può permettersi. Un server fittizio fa esattamente ciò che dice lo scenario, ogni volta, in millisecondi. Il compito della suite è catturare i nostri bug, non il tempo atmosferico di Google.
Un client di posta testato come se conservasse qualcosa che conta. MailVault archivia la tua posta in file standard sul tuo stesso disco, e ogni release deve prima guadagnarsi il passaggio attraverso questa suite.
Visita MailVault →La macchina sotto la scrivania non è la macchina su cui lavoriamo
Ecco la parte che ha cambiato la vita quotidiana. Un'esecuzione end-to-end completa è pesante: compila il frontend React, compila il backend Rust, poi apre e chiude l'applicazione decine di volte: una sessione nuova per ogni file di spec, quattordici file di spec nella sola suite connessa. Esegui tutto questo sulla macchina di sviluppo e puoi guardare le finestre rubarsi il focus mentre la CPU si dimentica che esisti. Nessuno scrive codice mentre succede.
Quindi le esecuzioni non avvengono lì. Un piccolo Mac mini Apple Silicon sta sulla rete locale senza fare nient'altro. È raggiungibile solo via SSH, solo dall'interno della rete, e conserva un clone del repository e della toolchain. Quando una funzionalità è pronta a dimostrare il proprio valore, l'esecuzione viene inviata al mini con un unico comando, e la macchina di sviluppo torna a costruire la cosa successiva mentre il mini compila, avvia e clicca attraverso quella corrente.
Le due attività si sovrappongono invece di alternarsi, il che sembra un dettaglio finché non conti quante volte al giorno una suite gira mentre le funzionalità vengono sviluppate. Ogni sessione sul mini avvia l'app a freddo, con stato e server nuovi, il che è anche un test onesto dell'avvio a freddo, su hardware che è comodamente più lento della macchina su cui l'app è stata scritta. Se una funzionalità è lenta sul mini, non è un problema del mini. È un'anteprima del portatile vecchio di quattro anni di qualcuno.
L'output di un fallimento torna indietro nello stesso modo in cui tornerebbe in locale: quale spec, quale asserzione, cosa mostrava davvero l'interfaccia. Correggi, invia di nuovo. Il ciclo è noioso, ed essere noioso è tutto il punto.
Cosa cattura questo che gli unit test educatamente si perdono
Le migliori scoperte della suite non stanno mai nel livello che coprirebbe un unit test. Vivono nelle giunture: un thread aperto dalla posta in arrivo che mostra il corpo della risposta della casella sbagliata, un messaggio spostato che ha lasciato il server ma non la lista, un'azione di archiviazione che ha funzionato ma ha ridisegnato l'icona della riga sbagliata. Ognuno di questi casi è dato da tre sottosistemi che concordano nell'essere individualmente corretti e collettivamente sbagliati. L'unico test che li cattura è quello che fa ciò che fa l'utente.
Ecco perché le spec sono scritte come frasi da utente ("marks a selected row as read and repaints it") invece che come nomi di funzione. Quando una fallisce, il report si legge come un bug che segnalerebbe una persona.
Esegui tu stesso la suite
MailVault è open source, e la suite gira interamente contro i server fittizi: nessun account, nessuna credenziale, niente da configurare. Se hai Node 18 o più recente e una toolchain Rust installati:
git clone https://github.com/GraphicMeat/mail-vault-app
cd mail-vault-app
npm install
cargo install tauri-webdriver-automation
npm run test:e2e
Il cargo install fornisce tauri-wd, il ponte WebDriver che la suite avvia per controllare l'app. L'ultimo comando fa il resto: compila il frontend in modalità test, compila il backend Rust con la funzionalità webdriver e avvia i server IMAP fittizi, e pilota l'app attraverso ogni spec. La prima esecuzione passa la maggior parte del tempo nel compilatore Rust; dopodiché, npm run test:e2e:ui e npm run test:e2e:connected riutilizzano la build ed eseguono direttamente le loro suite.
Mentre gira, la finestra dell'app si aprirà, cliccherà da sola, e si chiuderà, più e più volte. Il che è inquietante oppure è proprio il punto, a seconda di come ti senti riguardo a un robot che legge la posta.
Cosa non abbiamo ancora risolto
L'onestà costa meno del marketing, quindi: i server fittizi parlano l'IMAP che abbiamo implementato, non ogni dialetto che Gmail e Outlook improvvisano, e l'accesso OAuth contro i provider reali è ancora verificato da una mano umana. Le suite di regressione visiva e di backup girano ancora manualmente invece che a ogni invio. E il mini esegue una suite alla volta, in sequenza: sedici gigabyte di memoria sono un budget, e preferiamo un'esecuzione affidabile a due instabili. Tutte e tre sono nella lista, in quell'ordine.
Nel frattempo le funzionalità continuano ad arrivare per la via lenta: scritte, inviate, cliccate da un piccolo computer che non si annoia, e rilasciate solo quando smette di trovare cose. La tua posta merita almeno questa cerimonia.
La tua posta, archiviata localmente, da un'app che viene testata più duramente di quanto venga promossa. Gratuita e open source per macOS e Linux.
Visita MailVault →