Booleani

I due valori booleani sono: "true" (vero) e "false" (falso). Rispettivamente #t o #true e #f o #false in Guile.

Nel contesto di un test condizionale, "true" è una qualsiasi espressione che non sia #f (o #false).

Ti invito a creare una nuova directory nello spazio di lavoro dedicata a questo capitolo: ~/Workspace/guile-handbook/booleans.

Scrivere il primo test

Crea il file booleans-test.scm:

(use-modules (srfi srfi-64)
             (booleans))

(test-begin "harness")

(test-equal "true-inverted-returns-false"
  #f
  (boolean-invert #t))
  
(test-end "harness")

Provare ed eseguire il test

guile -L . booleans-test.scm

Errore di compilazione!

;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;; WARNING: compilation of /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm failed:
;;; no code for module (booleans)

Scrivere la quantità minima di codice per l'esecuzione del test e controllare l'output del test non riuscito

Gli errori di compilazione possono essere visti come test "red". Nella tradizione del TDD, è essenziale aggiungere solo il minimo indispensabile di codice per correggere questi errori.

Qui, il compilatore indica che il modulo booleans non esiste. Lo creo per correggere questo errore e riavvio immediatamente il test.

Crea il file booleans.scm:

(define-module (booleans))

Si verifica un nuovo errore! Questa volta compila, ma mi viene segnalato che la variabile boolean-invert non è collegata (cioè non è definita). La aggiungo al mio modulo, creato in precedenza, e rieseguo il test.

Edita il file booleans.scm:

(define-module (booleans))

(define-public (boolean-invert bool)
  0)

Non ci sono più errori o avvertimenti in fase di compilazione. Il test fallisce e si può verificare il motivo del fallimento nel file report harness.log. Una rapida occhiata conferma che la ragione del fallimento è che il test true-inverted-returns-false si aspetta un valore #f mentre la procedura boolean-invert restituisce sempre 0.

$ cat harness.log 
%%%% Starting test harness
Group begin: harness
Test begin:
  test-name: "true-inverted-returns-false"
  source-file: "booleans-test.scm"
  source-line: 6
  source-form: (test-equal "true-inverted-returns-false" #f (boolean-invert #t))
Test end:
  result-kind: fail
  actual-value: 0
  expected-value: #f
Group end: harness
# of unexpected failures  1

Scrivere abbastanza codice per farlo passare

Modifico la procedura boolean-invert in modo che restituisca il valore #f come previsto dal test.

Modifica il file booleans.scm:

(define-module (booleans))

(define-public (boolean-invert bool)
  #f)

L'esecuzione del test visualizza il seguente messaggio:

$ guile -L . booleans-test.scm
;;; note: source file /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;;       newer than compiled /home/jeko/.cache/guile/ccache/3.0-LE-8-4.3/home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;; compiled /home/jeko/.cache/guile/ccache/3.0-LE-8-4.3/home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm.go
%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      1

Il test è stato superato!

Refactor

Non c'è molto da fare per così poco codice.

Docstrings

Colgo l'occasione per parlarti delle docstrings. Queste stringhe forniscono una piccola spiegazione all'utente, se necessario:

  • in REPL, con il comando ,describe.
  • in Emacs

Si tratta di stringhe collocate in seconda posizione nell'elenco dei parametri quando si definisce una variabile o una procedura con define, define* o define-public (e via dicendo).

Aggiungiamo una docstring alla procedura boolean-invert!

Modifica il file booleans.scm:

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  #f)

Scrivere il primo test

Il primo test ha verificato che la chiamata alla procedura boolean-invert con il valore #t restituisce #f. Il prossimo test verificherà la situazione inversa.

Modifica il file booleans-test.scm:

(use-modules (srfi srfi-64)
             (booleans))

(test-begin "harness")

(test-equal "true-inverted-returns-false"
  #f
  (boolean-invert #t))
 
(test-equal "false-inverted-returns-true"
  #t
  (boolean-invert #f))
  
(test-end "harness")

Provare a eseguire il test

$ guile -L . booleans-test.scm 

Viene compilato senza nessun problema!

Scrivere la quantità minima di codice per l'esecuzione del test e controllare l'output del test non riuscito.

È possibile vedere il nuovo test che fallisce.

booleans-test.scm:10: FAIL false-inverted-returns-true

Verifica che il motivo del fallimento sia quello atteso. Ossia, il test false-inverted-returns-true si aspetta il valore #t ma riceve #f.

Test begin:
  test-name: "false-inverted-returns-true"
  source-file: "booleans-test.scm"
  source-line: 10
  source-form: (test-equal "false-inverted-returns-true" #t (boolean-invert #f))
Test end:
  result-kind: fail
  actual-value: #f
  expected-value: #t

Questo è confermato.

Scrivere abbastanza codice per farlo passare

Aggiungiamo il codice minimo e indispensabile richiesto per passare il test:

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  (if bool
      #f
      #t))

Lanciamo ancora il test…

%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      2

Tutto perfetto!

Refactor

Come puoi intuire, esiste una procedura già pronta per l'inversione di un dato booleano:

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  (not bool))

Riavvio i test un'ultima volta per verificare che la modifica non abbia danneggiato nulla:

%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      2

Fatto.

Conclusione

Cosa è stato trattato in questo capitolo:

  • Abbiamo praticato maggiormente TDD
  • Nozioni sui booleani
  • Scrivere codice auto-documentato grazie alle docstrings