go语言中的测试

简介: go语言中的测试

go语言中的测试

测试文件的命名是有一套规则的,通常是某个文件相对应的测试文件,比如app.go的测试文件就是app_test.go

错误测试

错误测试,也是测试中最基础的一种,test首字母要大写,后面的函数(测试谁写谁)首字母也要大写。使用go test命令进行启动。

func TestXxx(t *testing.T){
    if xxx {
        t.Errorf("xxx")
    }
}

基准测试

所谓基准测试,指的是go语言提供的某个算法或者程序执行一定的次数,然后输出平均的执行时间这个就叫做基准测试

跟test一样B大写,Benchmark 后面的函数首字母也要大写。

func BenchmarkXxx(*testing.B){
    // 这里的b.N go会自动提供,次数不一定。
     for i := 0; i < b.N; i++ {
        // 这里就是要测试的内容
        rand.Int()
    }
}

如果想在多线程的环境中测试,go给出了一个例子:

func BenchmarkTemplateParallel(b *testing.B) {
    templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
    // 这里的 RunParallel函数是关键。
    b.RunParallel(func(pb *testing.PB) {
        var buf bytes.Buffer
        for pb.Next() {
            buf.Reset()
            templ.Execute(&buf, "World")
        }
    })
}

范例测试

范例测试的意思就是说,运行的结果要跟你提供的例子保持一致

func ExampleHello() {
    fmt.Println("hello")
    // Output: hello
}
func ExampleSalutations() {
    fmt.Println("hello, and")
    fmt.Println("goodbye")
    // Output:
    // hello, and
    // goodbye
}

这里是有固定形态的,//Output:是固定的用法,后面跟例子输出的结果。

main测试

测试来控制哪些代码在主线程上运行。

func TestMain(m *testing.M){
os.Exit(m.Run())
}

子测试

使用`t.Run()`可以进行子测试。
func TestTeardownParallel(t *testing.T) {
    // This Run will not return until the parallel tests finish.
    t.Run("group", func(t *testing.T) {
        t.Run("Test1", parallelTest1)
        t.Run("Test2", parallelTest2)
        t.Run("Test3", parallelTest3)
    })
    // <tear-down code>
}

关于子测试,命令行里的命令是不一样的:

go test -run ''      # Run all tests.
go test -run Foo     # Run top-level tests matching "Foo", such as "TestFooBar".
go test -run Foo/A=  # For top-level tests matching "Foo", run subtests matching "A=".
go test -run /A=1    # For all top-level tests, run subtests matching "A=1".

跳过测试

如果想跳过某些条件,可以使用t或者b.Skip()方法。

func TestTimeConsuming(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping test in short mode.")
    }
    ...
}

文件系统测试

这个包是啥意思呢?其实就是帮你模拟了一个文件系统,因为你比如要打开xx文件吧,你不需要单独真的去新建一个,使用这个文件系统的测试,就可以达到这个目的,这个包是testing/fstest

// 声明一个fstest.MapFs 对象,因为这个对象实现了fs.Fs接口
  //【func (fsys MapFS) Open(name string) (fs.File, error)】
  var ms fstest.MapFS
  // 如果这里不声明ms是这个对象,而是直接就初始化底层类型给ms,
  //下面函数要使用的ms就不是fstest.Mapfs对象,而是map[string]*fstest.MapFile对象
  // 当然你也可以直接不初始化,下面使用的时候显示转化一下即可
  // fstest.MapFs(ms) 即可。
  ms = make(map[string]*fstest.MapFile)
  mf1 := &fstest.MapFile{
    Data: []byte("test"),
    Mode: 30,
    ModTime: time.Now(),
    Sys: "12",
  }
  mf2 := &fstest.MapFile{
    Data: []byte("test1"),
  }
    // 前面是路径,后面是文件。这是一个模拟。
  ms["a/1"] = mf1
  ms["a/2"] = mf2
  fmt.Println(fstest.TestFS(ms,"a/1","a/2","a/3"))

这里多说一点,go里面的显示类型转化,刚才讲的一般使用的时候,type A int,A类型并不是int,只是它的底层是int,虽然它的一切操作都可以按照int来做,比如

type A int
     var a A
  //a+1 就等于1,
  // 但是它仍然是A类型不是int,要注意类型转化,只有一个地方go会自动的语法糖,就是return的时候
  func fast()A{
      return 1
  }
  // 这里 return的时候进行自动类型判断了,没有把1判断为int,而是判断为A类型了。这属于语法糖。

io测试

io测试包testing/iotest主要是实现了readers和writers,具体我们可以理解为,它实现了很多读取和写入。

黑盒测试

黑盒测试,使用的包是testing/quick

比如一个可以快速得到是否正确的函数

func main(){
  f := func() bool{
    return 1 == 2
  }
  quick.Check(f,nil)
}

首先,对于我们来说,qucik.Check()是这个盒子的外壳,我们只能看到它,f我们是看不到的,所以我们可以通过check得到f的返回bool结果。

下面这个函数就是可以黑盒看f f1 是否是一致的。

func main(){
  f := func() bool{
    return true
  }
  f1 := func()bool {
    return false
  }
  quick.CheckEqual(f,f1,nil)
}

http测试

http测试,意思就是当你需要一个服务的时候,不需要自己再写一个http服务,你只需要net/http/httptest包即可。

这个包大致可以分为三个内容

  1. request,请求
    注意此包并不是客户端的请求,这是服务端的请求。【客户端用postman】
func NewRequest(method, target string, body io.Reader) *http.Request

目标是RFC 7230“请求目标”:它可以是路径或绝对URL。如果目标是绝对URL,则使用URL中的主机名。否则,将使用“ example.com”。

method 空是get

2. response,响应

这个包就是生成一个响应。

func NewRecorder() *ResponseRecorder
  1. server,服务
    服务器是侦听本地接口上系统选择的端口的HTTP服务器,用于端到端HTTP测试。
// 在这段代码中,第一段是一个server,下面是一个客户端get请求。所以上面哪个server监听了本地的请求。
func main() {
  ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, client")
  }))
  defer ts.Close()
  res, err := http.Get(ts.URL)
  if err != nil {
    log.Fatal(err)
  }
  greeting, err := io.ReadAll(res.Body)
  res.Body.Close()
  if err != nil {
    log.Fatal(err)
  }
  fmt.Printf("%s", greeting)
}

性能分析

net/http/pprof 包提供了例如gc,内存,cpu等数据的性能分析包。

如果要使用这个功能,需要写入这个import _ "net/http/pprof"

以及将下面代码加入

go func() {
  log.Println(http.ListenAndServe("localhost:6060", nil))
}()

新开一个go的groutine,然后来进行性能分析。

https://golang.org/pkg/net/http/pprof/

go tool pprof -http=:6062 http://localhost:6060/debug/pprof/block
go tool pprof -http=:6062 http://localhost:6060/debug/pprof/goroutine
go tool pprof -http=:6062 http://localhost:6060/debug/pprof/cpus

使用这个命令,可以把数据的分析,使用浏览器打开,http跟的端口,是自己设定的,后面的是分析的具体参数,比如/block/heap 等。下面有个列表,最前面就是这些命令。

runtime/pprof

pprof的具体实现,所有类型的代码都可以使用。如果不是Web应用程序,建议使用该包。

类型 描述 备注
allocs 内存分配情况的采样信息 可以用浏览器打开,但可读性不高
blocks 阻塞操作情况的采样信息 可以用浏览器打开,但可读性不高
cmdline 显示程序启动命令及参数 可以用浏览器打开,但可读性不高
goroutine 当前所有协程的堆栈信息 可以用浏览器打开,但可读性不高
heap 堆上内存使用情况的采样信息 可以用浏览器打开,但可读性不高
mutex 锁争用情况的采样信息 可以用浏览器打开,但可读性不高
profile CPU 占用情况的采样信息 浏览器打开会下载文件
threadcreate 系统线程创建情况的采样信息 可以用浏览器打开,但可读性不高
trace 程序运行跟踪信息 浏览器打开会下载文件

http请求跟踪测试

net/http/trace包提供了监听http请求的各个过程的功能,我们来看一个例子

func main() {
    // 这里有一个新的request
  req, _ := http.NewRequest("GET", "http://example.com", nil)
    // 这里,有两个参数被监听
  trace := &httptrace.ClientTrace{
    GotConn: func(connInfo httptrace.GotConnInfo) {
      fmt.Printf("Got Conn: %+v\n", connInfo)
    },
    DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
      fmt.Printf("DNS Info: %+v\n", dnsInfo)
    },
  }
    // 将钩子放入这个http的请求之内,实现监听的效果。
  req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
  _, err := http.DefaultTransport.RoundTrip(req)
  if err != nil {
    log.Fatal(err)
  }
}
目录
相关文章
|
18天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
38 2
|
16天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
26 2
|
16天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
30 2
|
2天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
16 7
|
5天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
18 2
|
6天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
6天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
14 3
|
6天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
16 3
|
8天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
7天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
21 3