zoukankan      html  css  js  c++  java
  • go fail point

    最近了解了一些关于go failpoint的信息,现在想给大家分享出来。

    FailPiont 是用来在自动化测试中模拟故障的

    自动化测试中,经常需要模拟出一些故障情况,然后来测试我们的程序在这种故障情况下是否能按照我们的想法正常执行。一些故障比较容易的被测试代码模拟,但是有一些就相对困难,比如断网了,接口访问量超限了等。于是有大神就搞出来了FailPiont。

     Etcd做出了 gofail https://github.com/etcd-io/gofail

    gofail 是这样使用的

    func someFunc() string {
    	// gofail: var SomeFuncString string
    	// // this is SomeFuncStringcalled when the failpoint is triggered
    	// return SomeFuncString
    	return "default"
    }
    

      先写一段模拟故障的代码,然后把代码注释掉。在我们测试的时候,执行 gofail enable 命令,然后代码就成了这样

    func someFunc() string {
    	if vSomeFuncString, __fpErr := __fp_SomeFuncString.Acquire(); __fpErr == nil { defer __fp_SomeFuncString.Release(); SomeFuncString, __fpTypeOK := vSomeFuncString.(string); if !__fpTypeOK { goto __badTypeSomeFuncString} 
    		 // this is SomeFuncStringcalled when the failpoint is triggered
    		 return SomeFuncString; __badTypeSomeFuncString: __fp_SomeFuncString.BadType(vSomeFuncString, "string"); };
    	return "default"
    }
    

      还有一个生成的文件

    // GENERATED BY GOFAIL. DO NOT EDIT.
    
    package fail_point
    
    import "github.com/etcd-io/gofail/runtime"
    
    var __fp_SomeFuncString *runtime.Failpoint = runtime.NewFailpoint("fail_point", "SomeFuncString")
    

      然后我们再写一个测试代码

    func Test_someFunc(t *testing.T) {
    	// /Users/edz/Documents/dev/metrics/src/fail_point/
    	gofail.Enable("fail_point/SomeFuncString", `return("SomeFuncString")`)
    	defer gofail.Disable("fail_point/SomeFuncString")
    	tests := []struct {
    		name string
    		want string
    	}{
    		{name: "pass", want: "SomeFuncString"},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			if got := someFunc(); got != tt.want {
    				t.Errorf("someFunc() = %v, want %v", got, tt.want)
    			}
    		})
    	}
    }
    

      

    跑一下测试,通过了。

    但是这个gofail也有一些弊端的 ,比如是以注释存在,不易发现错误;而且生成的代码可读性很差。

    为了解决这些问题,pingcap公司做出了一个升级版的gofail==》failpiont

    failpiont的代码是这样的

    func pingCapFail() (string, failpoint.Value) {
    	failpoint.Inject("failpoint-name", func(val failpoint.Value) {
    		failpoint.Return("unit-test", val)
    	})
    	return "success", nil
    }
    
    func pingCapFail1() string {
    	failpoint.Inject("failpoint-name1", func() {
    		failpoint.Return("unit-test")
    	})
    	return "success"
    }
    
    func pingCapFail2(ctx context.Context) (string, failpoint.Value) {
    	failpoint.InjectContext(ctx, "failpoint-name2", func(val failpoint.Value) {
    		failpoint.Return("unit-test", val)
    	})
    	return "success", nil
    }
    

      

    我们可以看到,failpiont避免了使用注释来实现的弊端,而且可以注入一定的代码,来影响代码的执行。

    这样就可以在测试的时候,多个测试用例同时进行,不同用例有的会就触发故障,有的则会忽略故障。

    执行 failpoint-ctl enable 命令来启用

    这时候代码变成了这样

    func pingCapFail() (string, failpoint.Value) {
    	if val, ok := failpoint.Eval(_curpkg_("failpoint-name")); ok {
    		return "unit-test", val
    	}
    	return "success", nil
    }
    
    func pingCapFail1() string {
    	if _, ok := failpoint.Eval(_curpkg_("failpoint-name1")); ok {
    		return "unit-test"
    	}
    	return "success"
    }
    
    func pingCapFail2(ctx context.Context) (string, failpoint.Value) {
    	if val, ok := failpoint.EvalContext(ctx, _curpkg_("failpoint-name2")); ok {
    		return "unit-test", val
    	}
    	return "success", nil
    }
    

      可以看到这时候的代码的可读性也很高。

    然后我们写一些测试代码

    func Test_pingCapFail(t *testing.T) {
    	tests := []struct {
    		name  string
    		want  string
    		want1 failpoint.Value
    	}{
    		{name: "pingCapFail", want: "unit-test", want1: 5},
    	}
    	failpoint.Enable("fail_point/failpoint-name", "return(5)")
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			got, got1 := pingCapFail()
    			if got != tt.want {
    				t.Errorf("pingCapFail() got = %v, want %v", got, tt.want)
    			}
    			if !reflect.DeepEqual(got1, tt.want1) {
    				t.Errorf("pingCapFail() got1 = %v, want %v", got1, tt.want1)
    			}
    		})
    	}
    }
    
    func Test_pingCapFail1(t *testing.T) {
    	tests := []struct {
    		name string
    		want string
    	}{
    		{name: "pingCapFail1", want: "unit-test"},
    	}
    	failpoint.Enable("fail_point/failpoint-name1", "return(5)")
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			if got := pingCapFail1(); got != tt.want {
    				t.Errorf("pingCapFail1() = %v, want %v", got, tt.want)
    			}
    		})
    	}
    }
    
    func Test_pingCapFail2(t *testing.T) {
    	type args struct {
    		ctx context.Context
    	}
    	c := &gin.Context{}
    	failPoints := map[string]struct{}{
    		"a": {},
    		"b": {},
    		// "fail_point/failpoint-name2": {},
    	}
    	ctx := failpoint.WithHook(c, func(ctx context.Context, fpname string) bool {
    		_, found := failPoints[fpname] // Only enables some failpoints.
    		return found
    	})
    
    	tests := []struct {
    		name  string
    		args  args
    		want  string
    		want1 failpoint.Value
    	}{
    		{"pingCapFail2", args{ctx}, "success", nil},
    	}
    	failpoint.Enable("fail_point/failpoint-name2", "return(5)")
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			got, got1 := pingCapFail2(tt.args.ctx)
    			if got != tt.want {
    				t.Errorf("pingCapFail2() got = %v, want %v", got, tt.want)
    			}
    			if !reflect.DeepEqual(got1, tt.want1) {
    				t.Errorf("pingCapFail2() got1 = %v, want %v", got1, tt.want1)
    			}
    		})
    	}
    }
    

      

    运行一下,测试通过。

    希望对大家有用,谢谢。

  • 相关阅读:
    PHP 对Memcache的使用实例
    PHP Memcache 扩展安装
    Effective STL 读书笔记
    windows下安装和使用scrapy
    使用insert ignore来避免向数据库重复插入数据
    2017年末
    归并排序
    二叉树的中序遍历
    正则表达式
    tinymq学习小结
  • 原文地址:https://www.cnblogs.com/13579net/p/10937279.html
Copyright © 2011-2022 走看看