CLI interactive integration tests с expect и Go — часть 1

Тестирование интерактивных консольных приложение получилось довольно интересной задачей.

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

 

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *