Тестирование интерактивных консольных приложение получилось довольно интересной задачей.
Как пример напишем простейшие консольное приложение при помощи библиотеки survey:
package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var qs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Default: "red",
},
},
{
Name: "age",
Prompt: &survey.Input{Message: "How old are you?"},
},
}
func main() {
// the answers will be written to this struct
answers := struct {
Name string // survey will match the question and field names
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
Age int // if the types don't match, survey will convert it
}{}
// perform the questions
err := survey.Ask(qs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
}
Приложение взаимодействует с пользователе спрашивая различне вопросы и ожидает от пользовательского ввода.
Чтобы убедиться что приложение ведет себя так как нужно необходимо написать скрипт который и будет вводить подготовленные ответы и ожидать что приложение в итоге вернет то, что мы ожидаем. Такими вещами может заниматься утилита expect. Для Go даже есть несколько реализации: Google, Netflix.
К сожалению их не удалось завести для работы с конкретно библиотекой survey. Поэтому пришлось пользоваться оригинальным expect. Одно из удобств этой утилиты это autoexpect — она может генерировать готовый скрипт на основе записи ваших действий в терминале. Сначала соберем наше приложение в бинарный файл:
go build .
Теперь «покажем» как мы взаимодействуем с нашим приложением:
autoexpect ./survey
В итоге autoexpect создаст новый файл script.exp — если его запустить он выполнит все те же команды что делали вы до этого. Тем самым мы можем «записать» сценарии и прогонять их.
В итоге мы получили вот такой скрипт:
set timeout -1 spawn ./survey match_max 100000 expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0m\[?25l" send -- "t" expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0mt" send -- "e" expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0mte" send -- "s" expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0mtes" send -- "t" expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0mtest" send -- "\r" expect -exact "\[?25h\[0G\[2K\[1;92m? \[0m\[1;99mWhat is your name? \[0m\[36mtest\[0m\r \[0G\[2K\[1;92m? \[0m\[1;99mChoose a color:\[0m \[36m\[Use arrows to move, type to filter\]\[0m\r \[1;36m> red\[0m\r \[39m blue\[0m\r \[39m green\[0m\r \[?25l" send -- "\[B" expect -exact "\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1;92m? \[0m\[1;99mChoose a color:\[0m \[36m\[Use arrows to move, type to filter\]\[0m\r \[39m red\[0m\r \[1;36m> blue\[0m\r \[39m green\[0m\r " send -- "\r" expect -exact "\[?25h\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1A\[0G\[2K\[1;92m? \[0m\[1;99mChoose a color:\[0m\[36m blue\[0m\r \[0G\[2K\[1;92m? \[0m\[1;99mHow old are you? \[0m\[?25l" send -- "1" expect -exact "\[0G\[2K\[1;92m? \[0m\[1;99mHow old are you? \[0m1" send -- "\r" expect eof
В следующей части мы попробуем написать Go test — его задача будет запуск expect и дальнейшие проверки.