Тестирование интерактивных консольных приложение получилось довольно интересной задачей.
Как пример напишем простейшие консольное приложение при помощи библиотеки 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 и дальнейшие проверки.