модульный тест Голанга
Введение в модульное тестирование
Чтобы гарантировать качество кода, многие компании требуют написания модульных тестов. Вот два индикатора,
- Покрытие функций: количество вызовов функций/количество функций, обычно 100%.
- Покрытие линии: количество пройденных линий/общее количество функций, обычно требуется > 60%
С помощью модульного тестирования мы можем тестировать различные сценарии, чтобы контролировать качество самих исследований и разработок. Компания, в которой я сейчас работаю, предъявляет высокие требования к модульному тестированию, и есть тенденция заменять тестирование.
go test
- Тест go обычно использует xxx_test.go в качестве имени файла, а xxx не требует измерения имени файла.
- TestMain как тест инициализации
- Testxxx(t* testing.T)
- go test для запуска модульных тестов
- go test --v --test.run funcName может указать единственный метод для тестирования
Создайте клиентский пакет, например, структура пакета выглядит следующим образом
.
├── client.go
├── client_test.go
func TestUser(t *testing.T) {
var u = &User{Age: 15, Name: "alice"}
if u.Age != 15 {
t.Error("age err")
}
if u.Name != "bob" {
t.Error("name err")
}
}
Поскольку имя не соответствует ожиданиям, появится следующее приглашение
--- FAIL: TestUser (0.00s)
client_test.go:28: name err
FAIL
exit status 1
FAIL test/mocktest 0.005s
go convey
goconvey может очень хорошо поддерживать настройку и демонтаж, goconvey может выполнять инициализацию и уничтожение состояния перед запуском одного тестового примера. goconvey также имеет много определенных функций утверждения, которые можно использовать напрямую, и вы можете настроить функцию утверждения. Обычно используются следующие утверждения:
var (
ShouldEqual = assertions.ShouldEqual
ShouldNotEqual = assertions.ShouldNotEqual
ShouldBeGreaterThan = assertions.ShouldBeGreaterThan
ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo
ShouldBeLessThan = assertions.ShouldBeLessThan
ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo
ShouldBeBetween = assertions.ShouldBeBetween
ShouldNotBeBetween = assertions.ShouldNotBeBetween
ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual
ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual
ShouldContainSubstring = assertions.ShouldContainSubstring
ShouldNotContainSubstring = assertions.ShouldNotContainSubstring
ShouldPanic = assertions.ShouldPanic
ShouldBeError = assertions.ShouldBeError
)
Пример использования:
func TestUser(t *testing.T) {
convey.Convey("TestUser", t, func() {
var u = &User{Age: 15, Name: "alice"}
convey.So(u.Age, convey.ShouldEqual, 15)
convey.So(u.Name, convey.ShouldEqual, "bob")
})
}
Поскольку имя не соответствует ожиданиям, появится следующее приглашение
Line 30:
Expected: 'bob'
Actual: 'alice'
(Should be equal)
mock
что такое насмешка
При модульном тестировании, что, если в процессе есть сторонние зависимости? Например, при выплате кредита требуется квота пользователя, а информация о квоте существует в другом микросервисе и должна быть извлечена с помощью RPC. Чтобы решить такой сценарий, мы можем использовать mock таким образом. Проще говоря, макет — это ввод, который может указывать зависимый интерфейс. Вывод можно понимать как фиксированные данные, вставленные заранее, чтобы процесс мог работать нормально.
Насмешка с насмешкой
предел:
- Вы можете только издеваться над интерфейсом
руководство
- Установите mockery: зайдите на github.com/vektra/mockery/.../
- mockery -name=имя интерфейса, сгенерировать каталог моков.
определение интерфейса rpc, реализация интерфейса
client.go:
package rpc
//go:generate mockery -name=Client
type Client interface {
Get(key string) (data interface{}, err error)
}
type ClientImpl struct {
Ct Client
}
func (p *ClientImpl) Get(key string) (data interface{}, err error) {
if mockCondition {
return p.Ct.Get(key)
}
// real logic
}
client_test.go
package rpc
import (
"fmt"
"test/mocktest/mocks"
"testing"
)
type User struct {
Age int
Name string
}
func TestMock(t *testing.T) {
convey.Convey("TestMock", t, func() {
mc := &mocks.Client{}
var u = &User{Age: 15, Name: "alice"}
mc.On("Get", "alice").Return(u, nil)
ci.Ct = mc
data, err := ci.Get("alice")
convey.So(data.Age, convey.ShouldEqual, 15)
}
}