• Golang之应用测试


    Go 应用测试

    测试的覆盖率

    命令:

    go test ./ -v -cover
    

    在《Go Web 编程》一书中,有以下结论:

    image-20201215213156322

    这并不是绝对的,测试文件可以在不同的包,进行测试也是不会出现问题的。

    但是这样的说法引起了我的兴趣。

    果然,执行测试的时候添加参数-cover的时候,如果不在同一个包,将会输出以下内容:

    coverage: [no statements]
    

    是没有办法得到,相关测试代码覆盖率的数据的。


    如果测试文件和被测试文件处于同一包下,才可以得到测试代码覆盖率相关数据的输出。

     coverage: 95.0% of statements
    

    并行测试

    命令:

    go test ./ -v -cover -parallel 3
    

    -parallel 3表示最多希望 3个测试并行执行。

    并发测试,利用多核优势,使用 Parallel 方法的函数必须 > 1,否则无法使用并发优势只有一个测试函数使用 Parallel 是没有效果的。

    可通过运行以下代码,并对比测试时间即可验证结论:

    package main
    
     import (
       "testing"
       "time"
     )
    
     func TestParallel_1(t *testing.T) {
       t.Parallel()
       time.Sleep(1 * time.Second)
     }
    
     func TestParallel_2(t *testing.T) {
       t.Parallel()
       time.Sleep(2 * time.Second)
     }
    
     func TestParallel_3(t *testing.T) {
       t.Parallel()
       time.Sleep(3 * time.Second)
     }
    

    基准测试

    命令:

    go test -v -cover -bench="BenchmarkQueryUser" -run x
    

    上面的命令既运行了基准测试,也运行了功能测试。如果需要,用户也可以通过运行标志-run来忽略功能测试。-run标志用于指定需要被执行的功能测试用例,如果用户把一个不存在的功能测试名字用作-run标志的参数,那么所有功能测试都将被忽略。

    上面的命令中使用-run x,如果测试中不存在任何名字为x的功能测试用例,因此所有功能测试都不会被运行。

    基准测试函数格式为:

    func BenchmarkXX×(*testing. B){ ... }
    

    举例:

    //基准测试
    func BenchmarkQueryUser(b *testing.B)  {
    	for i := 0; i < b.N; i++ {
    		user,err := userinfo.QueryUserById(1)
    		if err != nil{
    			b.Fatal("测试不通过,出现异常,err :",err)
    		}else {
    			fmt.Println("查询到数据:Result User=",*user)
    		}
    	}
    }
    

    输出如下:

    BenchmarkQueryUser-8        4711            282508 ns/op
    PASS
    coverage: 0.0% of statements
    ok      Go_test/src/unitTest    1.416s
    

    注意for循环里的 b.N表示循环块内的语句将执行b.N次。

    在进行基准测试时,测试用例的迭代次数是由Go自行决定的,虽然用户可以通过限制基准测试的运行时间达到限制迭代次数的目

    的,但用户是无法直接指定迭代次数的——测试程序将进行足够多次的迭代,直到获得一个准确的测量值为止。

    HTTP 测试

    Go不经为单元测试提供了包,还为Go Web应用提供了专有的包--testing-httptest包。

    Go Web应用的单元测试可以通过testing/httptest包来完成。这个包提供了模拟一个Web服务器所需的设施,用户可以利用net/http包中的客户端函数向这个服务器发送HTTP请求,然后获取模拟服务器返回的HTTP响应。

    例子:

    package main
    
    import (
    	"encoding/json"
    	"net/http"
    	"net/http/httptest"
    	"strings"
    	"testing"
    )
    
    func TestHandleGet(t *testing.T) {
    	mux := http.NewServeMux()   //创建一个多路复用器,用于运行测试
    	mux.HandleFunc("/post/", handleRequest)  //绑定需要测试的处理器
    
    	writer := httptest.NewRecorder()   //创建记录器,用于获取HTTP响应
    	request, _ := http.NewRequest("GET", "/post/1", nil)   //为被测试的处理器创建相应的请求
    	mux.ServeHTTP(writer, request)  //发送测试请求
    
        //对请求返回的响应结果进行检查处理
    	if writer.Code != 200 { 
    		t.Errorf("Response code is %v", writer.Code)
    	}
    	var post Post
    	json.Unmarshal(writer.Body.Bytes(), &post)
    	if post.Id != 1 {
    		t.Errorf("Cannot retrieve JSON post")
    	}
    }
    
    //与上面的测试方法同理
    func TestHandlePut(t *testing.T) {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/post/", handleRequest)
    
    	writer := httptest.NewRecorder()
    	json := strings.NewReader(`{"content":"Updated post","author":"Sau Sheong"}`)
    	request, _ := http.NewRequest("PUT", "/post/1", json)
    	mux.ServeHTTP(writer, request)
    
    	if writer.Code != 200 {
    		t.Errorf("Response code is %v", writer.Code)
    	}
    }
    

    以上例子中,每个测试用例都会独立运行并启动各自独立的用于测试的Web服务器。

    程序需要创建一个多路复用器并将handleRequest处理器与其进行绑定。除此之外,为了获取服务器返回的HTTP响应,程序使用httptest.New Recorder函数创建了一个 ResponseRecorder结构,这个结构可以把响应存储起来以便进行后续的检查。

    image-20201216171429956

    Go 的testing包允许用户通过TestMain 函数,在进行测试时执行相应的预设( setup )操作或者拆卸( teardown)操作。一个典型的TestMain 函数看上去是下面这个样子的:

    func TestMain (m *testing.M){
    	setUp ()
    	code := m. Run ( )tearDown ()
    	os.Exit (code)
    }
    

    setUp函数和tearDown函数分别定义了测试在预设阶段以及拆卸阶段需要执行的工作。需要注意的是,setUp函数和tearDown函数是为所有测试用例设置的,它们在整个测试过程中只会被执行一次,其中setup 函数会在所有测试用例被执行之前执行,而tearDown函数则会在所有测试用例都被执行完毕之后执行。至于测试程序中的各个测试用例,则由testing.M结构的Run方法负责调用,该方法在执行之后将返回一个退出码( exit code),用户可以把这个退出码传递给os.Exit函数。

    测试替身、依赖注入

    测试替身( test double)是一种能够让单元测试用例变得更为独立的常用方法。当测试不方便使用实际的对象、结构或者函数时,我们就可以使用测试替身来模拟它们。因为测试替身能够提高被测试代码的独立性,所以自动单元测试环境经常会使用这种技术。

    实现测试替身的一种设计方法是使用依赖注入(dependency injection)设计模式。这种模式迪过向被调用的对象、结构或者函数传人依赖关系,然后由依赖关系代替被调用者执行实际的操作,以此来解耦软件中的两个或多个层( layer ),而在Go语言当中,(被传入的依赖关系通常会是一种接口类型。接下来,就让我们来看看,如何在第7章介绍的简单Web服务中使用依赖注入设计模式。

    具体看图内的解耦过程:

    原流程:

    image-20201216170322579

    自顶向下进行解耦依赖关系:

    image-20201216171447925

  • 相关阅读:
    【C#/WPF】限制GridSplitter分隔栏的滑动范围
    【C#】访问泛型中的List列表数据
    【C#学习笔记】反射的简单用法
    【C#】获取泛型<T>的真实类型
    【Unity】关于发射子弹、导弹追踪的逻辑
    【转】【Unity】四元数(Quaternion)和旋转
    【Unity】UGUI的Text各种小问题
    【火狐FireFox】同步失败后,书签被覆盖,如何恢复书签
    【转】【Unity】实现全局管理类的几种方式
    【Unity】动态调用其他脚本的函数
  • 原文地址:https://www.cnblogs.com/l1ng14/p/14145091.html
Copyright © 2020-2023  润新知