введение
- Узнайте о здравом смысле модульного тестирования go
- Зачем писать модульные тесты
- Как писать юнит-тесты в джин-фреймворке
- Как элегантно писать модульные тесты в джине
1. Здравый смысл модульного тестирования
-
Имя файла модульного теста должно быть xxx_test.go (где xxx — программа бизнес-логики).
-
Делится на базовый тест, эталонный тест, кейс-тест
-
Имя функции базового теста должно быть Testxxx (xxx может использоваться для идентификации функций бизнес-логики).
-
Контрольные показатели должны отображаться как имена функций BenchmarkXXX.
-
В кейс-тесте должно быть имя функции ExampleXXX вне строки.
-
Параметры функции модульного тестирования должны быть t *testing.T (требуется средой тестирования)
-
Тестовая программа и тестируемые программные файлы находятся в пакете (можете не трогать, я положу отдельный каталог с тестами)
-
Файлы тестовых случаев не будут участвовать в обычной компиляции исходного кода и не будут включены в исполняемые файлы.
-
Этот пакет должен быть импортирован и протестирован
-
Многие люди сказали общие команды, я предоставлю документ
2. Зачем писать модульные тесты
Убедитесь в качестве кода, убедитесь, что каждая функция работает, результат работы правильный, убедитесь, что производительность написанного кода хорошая, а использование модульных тестов очень интуитивно понятно для повышения эффективности разработки, вам не нужно компилировать свой кодируйте каждый раз, а затем переходите к почтальону для имитации тестирования параметров, модуль может имитировать http-запросы и может помочь вам выполнить тестирование производительности и покрытие кода, экспортировать отчеты о тестировании и т. д.
3. Как писать юнит-тесты во фреймворке gin
TestMain
При написании тестов иногда требуется дополнительная настройка или демонтаж до или после теста; иногда тест также должен контролировать код, работающий в основном потоке. Для поддержки этих потребностей пакет тестирования предоставляет функцию TestMain:
package tests
import (
"testing"
"fmt"
"os"
"github.com/gin-gonic/gin"
"go-api/config"
)
func setup() {
gin.SetMode(gin.TestMode)
fmt.Println(config.AppSetting.JwtSecret);
fmt.Println("Before all tests")
}
func teardown() {
fmt.Println("After all tests")
}
func TestMain(m *testing.M) {
setup()
fmt.Println("Test begins....")
code := m.Run() // 如果不加这句,只会执行Main
teardown()
os.Exit(code)
}
тестовый пример джин-юнит
package main
func setupRouter() *gin.Engine {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
return r
}
func main() {
r := setupRouter()
r.Run(":8080")
}
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/ping", nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
4. Как элегантно писать юнит-тесты в джине
Использование табличных тестов
func TestFib(t *testing.T) {
var fibTests = []struct {
in int // input
expected int // expected result
}{
{1, 1},
{2, 1},
{3, 2},
{4, 3},
{5, 5},
{6, 8},
{7, 13},
}
for _, tt := range fibTests {
actual := Fib(tt.in)
if actual != tt.expected {
t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
}
}
}
Инкапсулировать несколько тестовых помощников
package tests
import (
"io"
"net/http"
"net/http/httptest"
"bytes"
"fmt"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"go-api/routes"
"go-api/tool"
)
type TestCase struct {
code int //状态码
param string //参数
method string //请求类型
desc string //描述
showBody bool //是否展示返回
errMsg string //错误信息
url string //链接
content_type string //
ext1 interface{} //自定义1
ext2 interface{} //自定义2
}
func NewBufferString(body string) io.Reader {
return bytes.NewBufferString(body)
}
func PerformRequest(mothod, url, content_type string, body string) (c *gin.Context, r *http.Request, w *httptest.ResponseRecorder) {
router := routes.InitRouter()
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
r = httptest.NewRequest(mothod, url, NewBufferString(body))
c.Request = r
c.Request.Header.Set("Content-Type", content_type)
router.ServeHTTP(w, r)
return
}
func call(t *testing.T,testcase []TestCase){
for k, v := range testcase {
_, _, w := PerformRequest(v.method, v.url, v.content_type, v.param)
//assert.Contains(t, w.Body.String(),fmt.Sprintf("\"error_code\":%d",v.code))
fmt.Println()
fmt.Printf("第%d个测试用例:%s", k+1, v.desc)
if v.showBody {
fmt.Printf("接口返回%s", w.Body.String())
fmt.Println()
}
s := struct {
Error_code int `json:"error_code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}{}
err := tool.JsonToStruct([]byte(w.Body.String()), &s)
assert.NoError(t, err)
assert.Equal(t, v.errMsg, s.Msg, "错误信息不一致")
assert.Equal(t, v.code, s.Error_code, "错误码不一致")
}
}
Использование подтестов
для управления вложенными тестами и порядком выполнения, разрешения зависимостей набора тестов
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
5. перейти к тестовой команде
- -bench regexp выполняет соответствующие тесты, например -bench=.;
- -benchtime s указывает время теста производительности, по умолчанию одна секунда
- -крышка включает тестовое покрытие;
- -run regexp запускает только те функции, которые соответствуют регулярному выражению, например -run=Array затем выполняет функции, содержащие начало Array;
- -v Показать подробные команды для теста.
- -cache=1 удалить кеш
- -args приводить аргументы после -args к тесту
- -json конвертировать вывод в json
- Параметр -o -o указывает сгенерированную бинарную исполняемую программу и выполняет тест, программа не будет удалена после теста
- -count указывает количество выполнений, по умолчанию 1
-bench
func BenchmarkMakeSliceWithoutAlloc(b *testing.B)
func BenchmarkMakeSliceWithPreAlloc(b *testing.B)
func BenchmarkSetBytes(b *testing.B)
-agrs
func TestArgs(t *testing.T) {
if !flag.Parsed() {
flag.Parse()
}
argList := flag.Args() // flag.Args() 返回 -args 后面的所有参数,以切片表示,每个元素代表一个参数
for _, arg := range argList {
if arg == "cloud" {
t.Log("Running in cloud.")
}else {
t.Log("Running in other mode.")
}
}
6. Конец
Поделиться с тобой
Общие команды и документацияадресный портал