Riempi os.Stdin per la funzione che legge da esso

Come faccio a riempire os.Stdin nel mio test per una funzione che legge da esso utilizzando uno scanner?

Richiedo un input da riga di comando dell’utente tramite uno scanner utilizzando la seguente funzione:

func userInput() error { scanner := bufio.NewScanner(os.Stdin) println("What is your name?") scanner.Scan() username = scanner.Text() /* ... */ } 

Ora, come posso testare questo caso e simulare un input da parte dell’utente? L’esempio seguente non funziona. Stdin è ancora vuoto.

 func TestUserInput(t *testing.T) { var file *os.File file.Write([]byte("Tom")) os.Stdin = file err := userInput() /* ... */ } 

Mocking os.Stdin

Sei sulla buona strada che os.Stdin è una variabile (di tipo *os.File ) che puoi modificare, puoi assegnargli un nuovo valore nei test.

È più semplice creare un file temporaneo con il contenuto che si desidera simulare come input su os.Stdin . Per creare un file temporaneo, utilizzare ioutil.TempFile() . Quindi scrivi il contenuto e cerca all’inizio del file. Ora puoi impostarlo come os.Stdin ed eseguire i tuoi test. Non dimenticare di pulire il file temporaneo.

Ho modificato il tuo userInput() con questo:

 func userInput() error { scanner := bufio.NewScanner(os.Stdin) fmt.Println("What is your name?") var username string if scanner.Scan() { username = scanner.Text() } if err := scanner.Err(); err != nil { return err } fmt.Println("Entered:", username) return nil } 

Ed è così che puoi testarlo:

 func TestUserInput(t *testing.T) { content := []byte("Tom") tmpfile, err := ioutil.TempFile("", "example") if err != nil { log.Fatal(err) } defer os.Remove(tmpfile.Name()) // clean up if _, err := tmpfile.Write(content); err != nil { log.Fatal(err) } if _, err := tmpfile.Seek(0, 0); err != nil { log.Fatal(err) } oldStdin := os.Stdin defer func() { os.Stdin = oldStdin }() // Restore original Stdin os.Stdin = tmpfile if err := userInput(); err != nil { t.Errorf("userInput failed: %v", err) } if err := tmpfile.Close(); err != nil { log.Fatal(err) } } 

Eseguendo il test, vediamo un risultato:

 What is your name? Entered: Tom PASS 

Vedi anche la domanda relativa al mocking del file system: codice di esempio per testare il filesystem in Golang

Il modo facile e preferito

Si noti inoltre che è ansible refactor userInput() non leggere da os.Stdin , ma invece potrebbe ricevere un io.Reader da cui leggere. Ciò renderebbe più robusto e molto più facile da testare.

Nella tua app puoi semplicemente passare os.Stdin ad esso, e nei test puoi passare qualsiasi io.Reader ad esso creato / preparato nei test, ad esempio usando strings.NewReader() , bytes.NewBuffer() o bytes.NewBufferString() .