I am having difficulty writing tests for this 3rd party library I am importing. I think this is because I want my CustomClient struct to have a client interface instead of the *banker.Client. This is making testing very difficult because it's hard to mock a *banker.Client. Any ideas how I can correctly turn this into an interface? So I can easily write mock tests against it and set up a fake client?
type CustomClient struct {
client *banker.Client //I want to change this to an interface
name string
address string
}
func (c *CustomClient) SetHttpClient(httpClient *banker.Client) { //I want to accept an interface so I can easily mock this.
c.client = httpClient
}
The problem is that banker.Client is a third party client I am importing with many structs and other fields inside of it. It looks like this:
type Client struct {
*restclient.Client
Monitor *Monitors
Pricing *Pricing
Verifications *Verifications
}
The end result is that my code looks like this:
func (c *CustomClient) RequestMoney() {
_, err := v.client.Verifications.GetMoney("fakeIDhere")
}
Given methods over fields on the struct, it sure wouldn't be a simple solution. However, we can try to minimize the lengthy test cases on the current package.
Add another layer (package) between your working package and banker. Simplifying the code in example to explain.
Let's say your banker package has the following code:
type Client struct {
Verification *Verification
}
type Verification struct{}
func (v Verification) GetMoney(s string) (int, error) {
...
}
Create another package that imports the banker and has interface defined, say bankops package:
type Bank struct {
BankClient *banker.Client
}
type Manager interface {
GetMoney(s string) (int, error)
}
func (b *Bank) GetMoney(s string) (int, error) {
return b.BankClient.Verification.GetMoney(s)
}
Note: The actual issue (test without interface) is still here in bankops package, but this is easier to test as we are only forwarding the result. Serves the purpose of unit tests.
Finally, in the current package (for me, it is main package), we can
type CustomClient struct {
client bankops.Manager
}
func (c *CustomClient) RequestMoney() {
_, err := c.client.GetMoney("fakeIDhere")
...
}
func main() {
client := &CustomClient{
client: &bankops.Bank{
BankClient: &banker.Client{
Verification: &banker.Verification{},
},
},
}
client.RequestMoney()
}
For working example, check in Playground.
You may add the setters or builders pattern as you were doing in your original code snippet to make the fields (like BankerClient) unexported.
I think it is impossible to make it into interface directly
because we should use the member variables of the Client.
How about making its member into interface?
For example,
for _, test := []struct{}{
testVerification VerificationInterface
}{{
testVerification: v.Client.Verifications
},{
testVerification: VerficationMock
}}{
// test code here
}
Related
I have a task when I need to pass a pointer across one application (a custom kubernetes controller) - it reads crd every 60 seconds and modifies that interface.
The problem is that when I pass a pointer I loose all methods available on that interface.
It's related to client-go/cache cache.Store
Is there a way to call methods on a pointer?
package main
import (
"k8s.io/client-go/tools/cache"
)
func main() {
store := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
writeObject(&store)
}
func writeObject(store *cache.Store) {
store.
}
Thanks
Your store is already a pointer, so passing a *cache.Store is possible, but not useful.
When accepting a cache.Store, you should be able to access its methods the normal way, e.g.
func writeObject(store cache.Store) {
err := store.Add(...)
}
I've been using Gin's ShouldBind() method to bind form data to a struct:
type UpdateUserInfoContext struct {
Country string `json:"country"`
EmailAddr string `json:"emailAddr"`
LoginID string `json:"loginID"`
UserName string `json:"username"`
}
func (h *handler) updateUserInfo(ctx *gin.Context) {
var json UpdateUserInfoContext
if err := ctx.ShouldBind(&json); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
h.service.UpdateUserPassword(json)
ctx.JSON(http.StatusOK, "success")
}
But now I need to build a large, dynamic UPDATE SQL based on what is and isn't present in the body of a POST request. Since ShouldBind() binds to a struct I can't iterate over the values in the body without using reflection. I figured an easier way would be to see if there's a method to bind the requests to a map instead of a struct. There is the context method PostFormMap(key string), however as far as I can tell from the example given here (https://github.com/gin-gonic/gin#another-example-query--post-form), this method requires the values correspond to to the argument key in the request body. Does anyone have any experience doing this? Thank you!
package main
import (
"fmt"
"encoding/json"
)
func main() {
strbody:=[]byte("{\"mic\":\"check\"}")
mapbody:=make(map[string]string)
json.Unmarshal(strbody,&mapbody)
fmt.Println(fmt.Sprint("Is this thing on? ", mapbody["mic"]))
}
//returns Is this thing on? check
https://play.golang.org/p/ydLuLsY8qla
I have a Character interface defined like:
type Character interface {
SomeFunction()
}
And a Player struct defined like:
type Player struct{}
func (r *Player) SomeFunction() { }
// Some fields and other functions....
Suppose I have a function defined as
func TakeInterface(characterValue Character) {
// Do something
}
The catch is, I want to pass in characterValue as a Player by address so that changes made to it will be made to the Player the caller passed in. In Java and C++, this is easy, but I can't seem to figure it out in Golang. I've tried something like,
func TakeInterface(characterValue &Character) {
// Do something that changes characterValue
}
and then passing in a Player pointer, but then I get the error *Character.Character is pointer to interface, not interface when I try to pass in an address.
How do I go about passing a Player by address to a function that takes a Character/Character pointer? I've been looking around but with no success. Thanks!
Have you tested your code? I'm guessing no, because it works the way you expect it to work. Just pass a pointer to a player when calling the function.
func main() {
p := new(Player)
TakeInterface(p)
}
Or
func main() {
p := Player{0}
TakeInterface(&p)
}
In go you can use a type or a pointer to a type as an argument to a function that takes an interface because both satisfy the interface. You can do both:
var p1 Player
p1.SomeFunction()
var p2 *Player
p2.SomeFunction()
You should do the tour of go: https://tour.golang.org/methods/10
Here is the whole code: https://play.golang.org/p/iQIChLCziG
The below does not work obviously:
Arbitrary := struct {
field1 string
field2 string
}{"a", "b"}
fmap := make(map[string]func(string) string)
fmap["fone"] = func(s string) string { fmt.Printf("function fone: %s", s) }
fmap["ftwo"] = func(s string) string { fmt.Printf("function ftwo: %s", s) }
// probably ok, as simple examples go, to this point where reflection needs to be used
// the below does not work
Arbitrary.fone = fmap["fone"]
Arbitrary.fone("hello")
The above is the core of what I'm trying to do: create a struct with values, and then create methods on the struct from a map of functions, or functions passed in. Basically I have a structure with data & ambiguous behavior that needs to be extended with methods unknown until creating the type.
I'm looking for the obvious & inevitable:
How to do this in Go
Why this shouldn't be done, or can't be done in Go (its possible with the reflect package, I just haven't found examples or reasoned thorough it yet)
How this should be done in Go (some sort of interface construct I've not figured out wholly. I've tried an interface which can handle the behavior; but it doesn't account for other behaviors that might be added, at the least I haven't figured out interface usage fully yet which is part of the issue)
If you're a person needing complexity here is the start of the actual task I'm trying to accomplish, making that structs behavior extendable.
I completely misunderstood the question.
NO, you can't create a new struct out of thin air and assign fields to it, also even if you could, for the love of everything that's holy, don't do that.
You can use multiple interfaces for example:
type Base interface {
Id() int //all structs must implement this
}
type Foo interface {
Base
Foo()
}
type Bar interface {
Base
Bar()
}
then make a map[string]Base, and you can assert the value later.
//leaving the original answer as a different approach to the problem.
While usually that kind of stuff is done using reflection, if you have a limited number of accepted "callbacks" you can use type assertion and an interface{} map, dropping the need for reflection.
var ctx = &Ctx{"Hello"}
var funcs = map[string]interface{}{
"m3": ctx.Do,
"m4": func(c *Ctx) { fmt.Println("ctx:", c) },
}
type Ctx struct {
Name string
}
func (c *Ctx) Do() {
fmt.Printf("Do: %+v\n", c)
}
func call(m string) {
if f, ok := funcs[m]; ok {
switch fn := f.(type) {
case func():
fn()
case func(*Ctx):
fn(&Ctx{"Hello world"})
default:
panic(fn)
}
}
}
playground
With the following being defined as is noted in the http library:
func Handle(pattern string, handler Handler)
type Handler interface { ServeHTTP(*Conn, *Request) }
How can I improve upon an existing handler (say, websocket.Draft75Handler for instance) by giving it an additional argument (and tell it what to do with the argument)?
I'm trying to create a handler that contains within it one end of a channel. It will use that channel to talk to some other part of the program. How can I get that channel into the the handler function?
Apologies if this is a silly question. I'm new at go and decided to learn by reading the tutorial and then jumping right into code. Thanks for any help!
If the type is a function, like websocket.Draft75Handler, you could wrap it in a closure:
func MyHandler(arg interface{}) websocket.Draft75Handler {
return func(c *http.ConnConn) {
// handle request
}
}
func main() {
http.Handle("/echo", MyHandler("argument"))
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.String())
}
}