代码组织
- 1 代码结构
- 2 重构与测试
- 2.1 安装测试功能
- 2.2 testify 的常用断言函数
- 3 表组测试
1 代码结构
所有的代码写在一个main.go文件里面,GO编译器也是可以正常执行的。但是当代码量很庞大时,很难进行维护。
Go Web 程序的代码组织
- 单文件——反模式
- 多文件——扁平模式
- 目录归类——分层模式
之前main.go文件里的内容:
4. 应用初始化 —— 数据库连接初始化、路由初始化、配置加载等…
5. 路由 —— 配置 URI 与控制器的对应关系
6. 控制器 —— 处理业务逻辑的 aboutHandler、homeHandler、articlesShowHandler 等
7. 数据库操作 —— 创建表、查询等
8. 数据模型 —— Article
9. 表单验证
10. 辅助函数 —— Int64ToString、RouteName2URL
11. 中间件
12. 错误处理等
2 重构与测试
2.1 安装测试功能
安装 stretchr/testify ,这是一个知名的第三方测试包,将用到它的断言(Assertion)功能。
go get github.com/stretchr/testify
编写测试文件tests/pages_test.go
后缀名 _test 是一个特殊标识,会告知 Go 编译器和工具链这是一个测试文件。Go 编译器在编译时会跳过这些文件,而工具 go test 在执行时默认会运行当前目录下所有拥有 _test 后缀名的文件。
package tests
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHomePage(t *testing.T) {
baseURL := "http://localhost:3000"
// 1. 请求 —— 模拟用户访问浏览器
var (
resp *http.Response
err error
)
resp, err = http.Get(baseURL + "/")
// 2. 检测 —— 是否无错误且 200
assert.NoError(t, err, "有错误发生,err 不为空")
assert.Equal(t, 200, resp.StatusCode, "应返回状态码 200")
}
2.2 testify 的常用断言函数
// 相等
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
// 是否为 nil
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
// 是否为空
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
// 是否存在错误
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool
// 是否为 0 值
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool
// 是否为布尔值
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool
// 断言长度一致
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool
// 断言包含、子集、非子集
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool)
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool)
// 断言文件和目录存在
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool
显示ok则成功
3 表组测试
Go 语言中,对于具备相同的测试逻辑的场景,可以使用简洁紧凑的 表组测试。
tests/pages_test.go
package tests
import (
"net/http"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAllPages(t *testing.T){
baseURL := "http://localhost:3000"
// 1. 声明加初始化测试数据
var tests = []struct {
method string
url string
expected int
}{
{"GET","/",200},
{"GET","/notfound",404},
{"GET","/about",200},
{"GET","/articles",200},
{"POST","/articles",200},
{"GET","/articles/create",200},
{"GET","/articles/3",200},
{"POST","/articles/3",200},
{"GET","/articles/3/edit",200},
{"POST","/articles/4/delete",404},
}
// 2. 遍历所有测试
for _, test := range tests {
t.Logf("当前请求 URL :%v \n", test.url)
var(
resp * http.Response
err error
)
// 2.1 请求以获取响应
switch{
case test.method == "POST":
data := make(map[string][]string)
resp,err = http.PostForm(baseURL + test.url, data)
default:
resp,err = http.Get(baseURL + test.url)
}
// 2.2 断言
assert.NoError(t,err,"请求"+test.url+" 时报错")
assert.Equal(t, test.expected, resp.StatusCode, test.url+" 应返回状态码"+strconv.Itoa(test.expected))
}
}
标准库里的辅助方法打印出来的数据:
t.Logf("当前请求 URL: %v \n", test.url)
使用 go test ./tests或者点击函数旁边的运行,前者不会显示t.Logf()里的内容,前者需要添加**-v** 详细打印。