How to use function interface with diff implementation - go

Im using interface which I want to mock one method in it function1 in test and I wasn't able to figure it out how is the best to do it that for prod code it will provide 1 value and for test provide some mock value , can someone please give example ? (edited)
this is the code:
https://play.golang.org/p/w367IOjADFV
package main
import (
"fmt"
"time"
)
type vInterface interface {
function1() bool
}
type mStruct struct {
info string
time time.Time
}
func (s *mStruct) function1() bool {
return true
}
func callSomething(si vInterface) bool {
return si.function1()
}
func (s *mStruct) vl1() bool {
s.time = time.Now()
s.info = "vl1->info"
return callSomething(s)
}
var currentVt1 mStruct
func main() {
vl1 := currentVt1.vl1()
fmt.Println(vl1)
}
The test is like this
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "my tests",
args: args{
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
But not sure how to mock it right ...
update
func Test_mStruct_vl1(t *testing.T) {
type fields struct {
info string
time time.Time
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "some test",
fields: struct {
info string
time time.Time
}{info: "myinfo", time: time.Now() },
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &mStruct{
info: tt.fields.info,
time: tt.fields.time,
}
if got := s.vl1(); got != tt.want {
t.Errorf("vl1() = %v, want %v", got, tt.want)
}
})
}
}

First you need a type (any type) that implements the vInterface interface. Here's a simple example:
type mockedVInterface struct {
value bool
}
func (m mockedVInterface) function1() bool {
return m.value
}
This is a simple enough implementation which we can control: we can tell what its function1() function should return by simply setting that value to its value field.
This mockedVInterface type is created solely for testing purposes, the production code does not need it. Put it in the same file where you have the test code (put it before Test_callSomething()).
And here's the testing code:
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "testing false",
args: args{
si: mockedVInterface{value: false},
},
want: false,
},
{
name: "testing true",
args: args{
si: mockedVInterface{value: true},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
Note that in this simple case we could also use a simple non-struct type that has bool as its underlying type like this:
type mockedVInterface bool
func (m mockedVInterface) function1() bool {
return bool(m)
}
And it works and testing code is also simpler:
tests := []struct {
name string
args args
want bool
}{
{
name: "testing false",
args: args{
si: mockedVInterface(false),
},
want: false,
},
{
name: "testing true",
args: args{
si: mockedVInterface(true),
},
want: true,
},
}
But this only works if the mockable interface has a single function with a single return value. In the general case a struct is needed.

Related

Optional array in struct

I want to make an array optional in struct and use it with an if else in a func.
type TestValues struct {
Test1 string `json:"test1"`
DefaultTests []string `json:"__tests"`
//DefaultTests *array `json:"__tests,omitempty" validate:"option"`
Test2 string `json:"__Test2"`
}
func (x *Controller) createTest(context *gin.Context, uniqueId string, testBody *TestValues) (*http.Response, error) {
if testBody.DefaultTags {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"defaultTests": testBody.DefaultTests,
"uniqueId": uniqueId,
})
} else {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"uniqueId": uniqueId,
})
}
...
}
When I run the code it tells me that DefaultTests is undefined array but I don't want this error to pop because DefaultTests can existe and sometimes it won't be present in the json that's why I want to make it optional. The if else part is not working too.
It's better to use len() when checking if an array is empty here.
if len(testBody.DefaultTests) > 0 {
...
}
Check the Zero value of the DefaultTests in struct below for more clarity on this behaviour
package main
import "fmt"
type TestValues struct {
Test1 string `json:"test1"`
DefaultTests []string `json:"__tests"`
//DefaultTests *array `json:"__tests,omitempty" validate:"option"`
Test2 string `json:"__Test2"`
}
func main() {
var tv = TestValues{Test1: "test"}
if len(tv.DefaultTests) > 0 {
fmt.Printf("Default Tests: %v\n", tv.DefaultTests)
} else {
fmt.Printf("Default Tests empty value: %v\n", tv.DefaultTests)
}
}
Here's my final code with Marc's answer
func (x *Controller) createTest(context *gin.Context, uniqueId string, testBody *TestValues) (*http.Response, error) {
if len(testBody.DefaultTags) > 0 {
postBody, err := json.Marshal(testBody.DefaultTags)
} else {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"uniqueId": uniqueId,
})
}
...
}

golangci-lint undeclared name: `getProxyURL` (typecheck)

Im new on golang and Im trying to _test my proxy func, the test passes correctly but when running golangci it gives me the error:
undeclared name: getProxyURL (typecheck)
if got := getProxyURL(tt.args.campaignCode, urls); got != tt.want {
^
func getProxyURL(campaignCode string, urls map[string]string) string {
if campaignURL, ok := urls[campaignCode]; ok {
return campaignURL
}
return "https://facebook.com"
}
_test
package main
import "testing"
func Test_getProxyURL(t *testing.T) {
type args struct {
campaignCode string
}
urls := make(map[string]string, 0)
urls["82383b80-056b-42e8-b192-9b0f33c4f46e"] = "https://google.com"
urls["negativeTest"] = "https://facebook.com"
tests := []struct {
name string
args args
want string
}{
{
name: "Given an invalid campaign code, we receive facebook url as result",
args: args{campaignCode: "negativeTest"},
want: "https://facebook.com",
}, {
name: "Given an valid campaign code, we receive google url as result",
args: args{campaignCode: "82383b80-056b-42e8-b192-9b0f33c4f46e"},
want: "https://google.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getProxyURL(tt.args.campaignCode, urls); got != tt.want {
t.Errorf("getProxyURL() = %v, want %v", got, tt.want)
}
})
}
}
I cant find the problem

golang patch string values on an object, recursive with filtering

Community,
The mission
basic
Implement a func that patches all string fields on an objects
details
[done] fields shall only be patched if they match a matcher func
[done] value shall be processed via process func
patching shall be done recursive
it shall also work for []string, []*string and recursive for structs and []struct, []*struct
// update - removed old code
Solution
structs
updated the structs to use (though this does not affect the actual program, i use this for completeness
type Tag struct {
Name string `process:"yes,TagName"`
NamePtr *string `process:"no,TagNamePtr"`
}
type User struct {
ID int
Nick string
Name string `process:"yes,UserName"`
NamePtr *string `process:"yes,UserNamePtr"`
Slice []string `process:"yes,Slice"`
SlicePtr []*string `process:"yes,SlicePtr"`
SubStruct []Tag `process:"yes,SubStruct"`
SubStructPtr []*Tag `process:"yes,SubStructPtr"`
}
helper func
Further we need two helper funcs to check if a struct has a tag and to print to console
func Stringify(i interface{}) string {
s, _ := json.MarshalIndent(i, "", " ")
return string(s)
}
func HasTag(structFiled reflect.StructField, tagName string, tagValue string) bool {
tag := structFiled.Tag
if value, ok := tag.Lookup(tagName); ok {
parts := strings.Split(value, ",")
if len(parts) > 0 {
return parts[0] == tagValue
}
}
return false
}
patcher - the actual solution
type Patcher struct {
Matcher func(structFiled *reflect.StructField, v reflect.Value) bool
Process func(in string) string
}
func (p *Patcher) value(idx int, v reflect.Value, structFiled *reflect.StructField) {
if !v.IsValid() {
return
}
switch v.Kind() {
case reflect.Ptr:
p.value(idx, v.Elem(), structFiled)
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
var sf = v.Type().Field(i)
structFiled = &sf
p.value(i, v.Field(i), structFiled)
}
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
p.value(i, v.Index(i), structFiled)
}
case reflect.String:
if p.Matcher(structFiled, v) {
v.SetString(p.Process(v.String()))
}
}
}
func (p *Patcher) Apply(in interface{}) {
p.value(-1, reflect.ValueOf(in).Elem(), nil)
}
how to use
func main() {
var NamePtr string = "golang"
var SubNamePtr string = "*secure"
testUser := User{
ID: 1,
Name: "lumo",
NamePtr: &NamePtr,
SubStruct: []Tag{{
Name: "go",
},
},
SubStructPtr: []*Tag{&Tag{
Name: "*go",
NamePtr: &SubNamePtr,
},
},
}
var p = Patcher{
// filter - return true if the field in struct has a tag process=true
Matcher: func(structFiled *reflect.StructField, v reflect.Value) bool {
return HasTag(*structFiled, "process", "yes")
},
// process
Process: func(in string) string {
if in != "" {
return fmt.Sprintf("!%s!", strings.ToUpper(in))
} else {
return "!empty!"
}
return in
},
}
p.Apply(&testUser)
fmt.Println("Output:")
fmt.Println(Stringify(testUser))
}
goplay
https://goplay.tools/snippet/-0MHDfKr7ax

Is there a good way to update structs passed to sub-tests in Golang

I am trying to write a table-driven test to test, say, if two orders passed in to a function are the same,
where Order could something like
type Order struct {
OrderId string
OrderType string
}
Now right now, my test looks like:
func TestCheckIfSameOrder(t *testing.T) {
currOrder := Order{
OrderId: "1",
OrderType: "SALE"
}
oldOrder := Order{
OrderId: "1",
OrderType: "SALE"
}
tests := []struct {
name string
curr Order
old Order
want bool
}{
{
name: "Same",
curr: currOrder,
old: oldOrder,
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := checkIfSameOrder(tt.curr, tt.old)
if got != tt.want {
t.Errorf("want %v: got %v", tt.want, got)
}
})
}
}
func checkIfSameOrder(currOrder Order, oldOrder Order) bool {
if currOrder.OrderId != oldOrder.OrderId {
return false
}
if currOrder.OrderType != oldOrder.OrderType {
return false
}
return true
}
What I want to do is to add a second test, where I change the OrderId on the currOrder or something and so that my tests slice looks like
tests := []struct {
name string
curr Order
old Order
want bool
}{
{
name: "Same",
curr: currOrder,
old: oldOrder,
want: true,
},
{
name: "Different OrderId",
curr: currOrder, <-- where this is the original currOrder with changed OrderId
old: oldOrder,
want: false,
},
}
Seems to me like I can't use a simple []struct and use something where I pass in a function, but I can't seem to find how to do that anywhere. I'd appreciate if anybody could point me in the right direction. Thanks!
If only the OrderId is different from each test, you can just pass the order id and construct oldOrder and currOrder based on that inside loop.
Sharing global variable that you mutate throughout processes has caused confusion all the time. Better initialize new variable for each test.
func TestCheckIfSameOrder(t *testing.T) {
tests := []struct {
name string
currId string
oldId string
want bool
}{
{
name: "Same",
currId: "1",
oldId: "1",
want: true,
},
{
name: "Different",
currId: "1",
oldId: "2",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
curr := Order{
OrderId: tt.currId,
OrderType: "SALE"
}
old := Order{
OrderId: tt.oldId,
OrderType: "SALE"
}
got := checkIfSameOrder(curr, old)
if got != tt.want {
t.Errorf("want %v: got %v", tt.want, got)
}
})
}
}

Mock inner function in golang

I want to mock function using interface and I was able to mock the first function callsomething
With great help from icza , Now it’s a bit more tricky . I want to test function vl1
With mock for function function1 , how it can be done.
https://play.golang.org/p/w367IOjADFV
package main
import (
"fmt"
"time"
"testing"
)
type vInterface interface {
function1() bool
}
type mStruct struct {
info string
time time.Time
}
func (s *mStruct) function1() bool {
return true
}
func callSomething(si vInterface) bool {
return si.function1()
}
func (s *mStruct) vl1() bool {
return callSomething(s)
}
var currentVt1 mStruct
func main() {
vl1 := currentVt1.vl1()
fmt.Println(vl1)
}
//——————————————————TESTS——————————————————
// This test is working as expected (as suggested by icza) for the function "callSomething"
// here we use mock interface and mock function which helps to mock function1
type mockedVInterface struct {
value bool
}
func (m mockedVInterface) fn1() bool {
return m.value
}
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "first value",
args: args{
si: mockedVInterface{value: false},
},
want: false,
},
{
name: "second value",
args: args{
si: mockedVInterface{value: true},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
//----Here is the test which a bit tricky
//I want to call to "s.vl1()" and test it with mock for "function1"
func Test_mStruct_vl1(t *testing.T) {
type fields struct {
info string
time time.Time
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "test 2",
fields: struct {
info string
time time.Time
}{info: "myinfo", time: time.Now() },
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &mStruct{
info: tt.fields.info,
time: tt.fields.time,
}
//here the test is starting
if got := s.vl1(); got != tt.want {
t.Errorf("mStruct.vl1() = %v, want %v", got, tt.want)
}
})
}
}
If you want to mock a function in Go you'll have to declare a variable that will hold the function value, have all callers of the function use the variable instead and then during tests you can switch up the function value for a mock implementation.
func callSomethingFunc(si vInterface) bool {
return si.function1()
}
var callSomething = callSomethingFunc
func (s *mStruct) vl1() bool {
return callSomething(s)
}
// ...
func Test_mStruct_vl1(t *testing.T) {
callSomething = func(si vInterface) bool { // set mock
// do mock stuff
}
defer func(){
callSomething = callSomethingFunc // set back original func at end of test
}()
// ...

Resources