Unit test in golang with structure - go

I use the VSCode generation for test file of my project,
currenlty it generate the folloing structure
tests := []struct {
name string
args args
wantOut ZTR
}{
name: "test123",
args: args{
ztrFile: "./testdata/ztrfile.yaml",
},
wantOut: “ZTR.Modules",
}
The test should cover parse of yaml and testing the properties
Here it calles to parse file
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotOut := parseFile(tt.args.ztrFile); !reflect.DeepEqual(gotOut, tt.wantOut) {
t.Errorf("parseFile() = %v, want %v", gotOut, tt.wantOut)
}
})
This is the struct
type Modules struct {
Name string
Type string
cwd string `yaml:”cwd,omitempty"`
}
Not sure what I need to put here to make it work, I try to play with the types but Im getting errors
{
name: "test123",
args: args{
mtaFile: "./testdata/ztrfile.yaml",
},
wantOut: “ZTR.Modules",
}
The errors I got is
message: 'cannot use "test123" (type string) as type struct { name string; args args; wantOut ZTR } in array or slice literal'
at: '41,3'
source: ''
code: 'undefined'

Your tests declaration is incorrect. You need to provide a slice of structs, but you're providing just keys/values:
tests := []struct {
name string
args args
wantOut ZTR
}{
name: "test123",
args: args{
mtaFile: "./testdata/ztrfile.yaml",
},
wantOut: “ZTR.Modules",
}
should be:
tests := []struct {
name string
args args
wantOut ZTR
}{
{
name: "test123",
args: args{
mtaFile: "./testdata/ztrfile.yaml",
},
wantOut: “ZTR.Modules",
},
}

Related

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

gomock: because: there are no expected calls of the method "Pod" for that receiver

I am trying to mock mysql, but occur error: "because: there are no expected calls of the method "Pod" for that receiver. "
I confirmed that I have generated the Pod method with the Mockgen tool,
Below is my code
func TestPodService_Create(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockFactory := store.NewMockFactory(ctrl)
mockPod := store.NewMockPodStore(ctrl)
pods := fake.FakePod(10)
mockPod.EXPECT().Create(gomock.Eq(context.TODO()), gomock.Eq(pods[0])).Return(nil)
type fields struct {
store store.Factory
redisCli redis.RedisCli
}
type args struct {
ctx context.Context
pod *model.Pod
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
// TODO: Add test cases.
{
name: "test case 1",
fields: fields{store: mockFactory,},
args: args{
ctx: context.TODO(),
pod: &pods[0],
},
wantErr: false,
},
}
for _, tt := range tests {
fmt.Printf("begin to test\n")
podService := &PodService{store: tt.fields.store}
err := podService.Create(tt.args.ctx, tt.args.pod)
assert.Equal(t, tt.wantErr, err!=nil)
}
}
You need to include this line in your TestPodService_Create():
mockPod.EXPECT().Pod(gomock.Any()).AnyTimes()
Adjust the gomock.Any() and .AnyTimes() for your desired goals.

How to use function interface with diff implementation

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.

Match array according to value

I'm using the following code to parse yaml and should get output as runners object and the function buildshould change the data structure and provide output according to below struct
type Exec struct {
NameVal string
Executer []string
}
This is what I have tried but I'm not sure how to replace
the hard-code values inside the function runner from the value I'm getting inside the yaml
return []Exec{
{"#mytest",
[]string{"spawn child process", "build", "gulp"}},
}
with the data from the parsed runner
This is all what I have tried any idea how it could be done?
package main
import (
"log"
"gopkg.in/yaml.v2"
)
var runContent = []byte(`
api_ver: 1
runners:
- name: function1
data: mytest
type:
- command: spawn child process
- command: build
- command: gulp
- name: function2
data: mytest2
type:
- command: webpack
- name: function3
data: mytest3
type:
- command: ruby build
- name: function4
type:
- command: go build
`)
type Result struct {
Version string `yaml:"api_ver"`
Runners []Runners `yaml:"runners"`
}
type Runners struct {
Name string `yaml:"name"`
Type []Command `yaml:"type"`
}
type Command struct {
Command string `yaml:"command"`
}
func main() {
var runners Result
err := yaml.Unmarshal(runContent, &runners)
if err != nil {
log.Fatalf("Error : %v", err)
}
//Here Im calling to the function with the parsed structured data which need to return the list of Exec
build("function1", runners)
}
type Exec struct {
NameVal string
Executer []string
}
func build(name string, runners Result) []Exec {
for _, runner := range runners.Runners {
if name == runner.Name {
return []Exec{
// this just for example, nameVal and Command
{"# mytest",
[]string{"spawn child process", "build", "gulp"}},
}
}
}
}
Assign the name of runners object to the struct Exec field for name and append the command list to the []string type field with the commands of the function that matched the name as:
func build(name string, runners Result) []Exec {
exec := make([]Exec, len(runners.Runners))
for i, runner := range runners.Runners {
if name == runner.Name {
exec[i].NameVal = runner.Name
for _, cmd := range runner.Type {
exec[i].Executer = append(exec[i].Executer, cmd.Command)
}
fmt.Printf("%+v", exec)
return exec
}
}
return exec
}
Working code on Playground

Golang Mutation with a list as a param variable (GRAPHQL)

Basically what im trying to do is send a list of string ex: ["aa","bb","vv"] into a graphql Mutation field, currently this is my Mutation Schema
"listTest": &graphql.Field{
Type: QueryMessageType,
Args: graphql.FieldConfigArgument{
"listNew": &graphql.ArgumentConfig{
Description: "Example List of Json String",
Type: graphql.NewList(graphql.NewNonNull(graphql.String)),
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
list := p.Args["listTest"].([]string)
return listTest(list)
},
},
and the Method listTest
func listTest(testing[]string) (*QueryMessage, error) {
fmt.Println(testing)
return &QueryMessage{
QueryBody: "nothing to do here",
}, nil
}
However when i do the request in INSOMNIA the response is:
{
"data": {
"listTest": null
},
"errors": [
{
"message": "interface conversion: interface {} is []interface {}, not []string",
"locations": []
}
]
}
and the request is this:
mutation{
listTest(listNew: ["aa","bb","vv"]){
querybody
}
}
can anyone tell me how to receive a List of String in my Go Server. Thanks! :)
UPDATE
When i call a fmt.Println(p.Args["listTest"])
the result is: [aa bb vv]
SOLVED
Following the instructions of the voted answer, the script now do his job. This is the final result:
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
var groupIDs []string
for _, gid := range p.Args["list"].([]interface{}) {
groupIDs = append(groupIDs, gid.(string))
}
for _, final := range groupIDs {
fmt.Println(final)
}
return listTest(groupIDs)
},
and in the console i got this:
aa
bb
vv
Your problem, according to the error message, is with this line:
list := p.Args["listTest"].([]string)
p.Args["listTest"] is returning []interface{}.
interface{} can store any other type. If you are familiar with java it's a little bit like Object.
The problem here is that you have your field from p.Args["listTest"] and you are trying to type assert it to []string. This would work if the value stored in args were interface{} (any). But it's not, p.Args (according to the error) holds []interface{}. This is a slice of interface values, where each of those can be anything (rather than a single interface value holding a slice of strings.)
Instead try ranging over that list of interfaces, and type asserting each value.
var strs []string
for _, v := range p.Args["list"].([]interface{}) {
strs = append(strs, v.(string))
}
Or investigate another way to set up the graphQL types so that you get the values back in a more useable way.

Resources