go fail piont

Recently learned some information about go failpiont, and now want to share it.

FailPiont is used to simulate a fault in the automatic test

Automated testing, often need to simulate some fault conditions, and then to test our procedures in this case whether a failure to perform properly in accordance with our ideas. Some failures are easier to test code simulation, but there are some relatively Kun Dan, such as a broken network interface traffic overrun the like. Then there are the great God to engage in out FailPiont.

 Etcd made gofail  https://github.com/etcd-io/gofail

gofail is used

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

  To write code for a simulated failure, then the code commented out. When we test execution gofail enable command, then the code has become so

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"
}

  There is also a generated file

// 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")

  Then we write a test code

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)
			}
		})
	}
}

  

Run some tests, passed.

 

But this gofail also has some drawbacks, such as the presence of the comment is not easy to find errors; and generated code readability is poor.

To solve these problems, pingcap company made a upgraded version of gofail == "failpiont

Such code is 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
}

  

We can see, failpiont avoid the drawbacks of using annotations to achieve, but also inject some code to affect the execution of the code.

This allows multiple simultaneous test cases in the test, different use cases and some will be triggered fault, some will ignore the fault.

 

Execution failpoint-ctl enable commands to enable

This time the code has become such

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
}

  This can be seen when the readability of the code is also high.

Then we write some test code

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)
			}
		})
	}
}

  

 

Run it, test.

I want to be useful, thank you.

 

Guess you like

Origin www.cnblogs.com/13579net/p/10937279.html
Go