Unit tests mock an interface from struct property - go

I want to mock a property from a struct that uses an interface so I don't get a nil pointer when the function arrives at that point.
This is the code:
type Server struct{
parser: Parser
}
type Parser interface{
SetProvider(p *Provider)
}
func (s *Server) doSomething(){
s.anotherAction()
// ...
// here it crashes because I haven't assigned anything to parser in serverMock
s.parser.SetProvider(&Provider{
name: "foo"
})
}
And this the test code:
var serverMock = &Server{
parser:
}
// mock SetProvider
func (s *Server) SetProvider(p *Provider) {
// some action
}
TestMyCustomTest(){
res, err := serverMock.doSomething()
expected := struct{
hobby: "code",
}
assert.Equal(t, &expected, res)
}
As you see I haven't assigned anything to parser: from var serverMock because I still don't know what should I do to make it work. I cannot assign a mockInterface to that parser field because it'll tell me that the original Server struct requires the Parser interface not a mock one and I think I need the &Server pointer for var serverMock = &Server because it's using other actions like s.anotherAction().

If i understand your intention correctly, you want to test Server's doSomething method and it depends on Parser.SetProvider method. So, what you want, instead of mocking Server, is to create mock struct that satisfies Parser interface and create Server instance that uses it. Here is example of such test:
type mockParser struct {
// You can add field here
}
// mockParser implements SetProvider(p *Provider) method so that it satisfies Parser interface
func (parser *mockParser) SetProvider(provider *Provider) {
// You can do something here
}
TestMyCustomTest(t *testing.T){
parser := mockParser{}
srv := &Server{parser: &parser}
// write your test
assert.Equal(t, ...)
}

Related

Gomock cannot use type map[string]*mockFoo as map[string]foo

I am using gomock, and I have this piece of example code that I wish to test.
type StructA struct {
client map[string]Foo
}
type Foo interface {
foo.methodFoo() string
}
func (a *structA) MethodA(name string) string {
client := a.client[name]
return client.methodFoo()
}
In my test, i've generated a mock for foo, called mockFoo. Used mockgen v1.6.0 to generate the mock for interface foo.
I have the test code as:
func Test_MethodA(t *testing.T) {
type fields struct {
client map[string]*mockFoo
}
tests := []struct {
fields fields
want string
}
{
// test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &StructA {
client: tt.fields.client //this has an error, saying that we cannot use map[string]*mockFoo as the type map[string]foo
}
...
})
}
}
TLDR is that map[string]*mockFoo cannot be used as the type map[string]Foo.
var foo1 Foo
var mockFoo1 *mockFoo
foo1 = mockFoo1 // no problem
var fooMap map[string]Foo
var mockFooMap map[string]*mockFoo
fooMap = mockFooMap // problematic
May I know if I'm doing anything wrong here or if this an expected behaviour? Thanks.
Based on your description, Foo is an interface, and *mockFoo implements the Foo interface. Thus, whenever a Foo is required, you can used *mockFoo.
The type map[string]Foo is not the same as the type map[string]*mockFoo. Both are concrete types (they are not interfaces), so you cannot substitute one for the other.
You can, however, use map[string]Foo, and put *mockFoo values into that map, and as far as I can see from your code, that's what you should do. Declare the map as map[string]Foo, and use *mockFoo values in it. If you need to refer to the values of the map as *mockFoo, you can type-assert the map value to get the *mockFoo value, that is:
fooMap=map[string]Foo{}
fooMap["key"]=mockFoo1
...
value:=foomap["key"].(*mockFoo)

Mock function without receiver

I have the file util.go:
func Foo(service *SomeService) error {
return helper(service)
}
func helper(service *SomeService) error {
...
}
I'm writing unit tests using testify, starting with Foo. I want to:
mock helper
assert mocked helper was called 1 time
I saw some promising solutions at https://stackoverflow.com/a/19168875/1661745, but not sure about them:
Method 1: pass helper as parameter of Foo. My doubt: testify needs a Mock struct to AssertNumberOfCalls, and here there is no struct.
Method 2: create a struct for Foo. My doubt: I don't know if it makes sense to make a struct for utils. Also requires more refactoring since callers of Foo would need a utils struct.
What's the best way to do this?
If you just want to test the args being called in helper, this is an approach that I have been using. The same test will also prove that your helper was called exactly once.
// Code
var originalFn = func(arg1, arg2 string) {
...
}
func Foo() {
originalFn(arg1,arg2)
}
// Tests
func TestFoo(t *testing.T) {
tempFn := originalFn
var fnArgs []string
originalFn = func(arg1, arg2) {
fnArgs = append(fnArgs, []string{arg1, arg2})
}
defer originalFn = tempFn
tests := []struct{
expected []string
}{
{
expected: []string{"arg1", "arg2"},
},
}
for _, tt:= range tests {
fnArgs := make([]string, 0)
Foo()
assert.Equal(t, tt.expected, fnArgs)
}
}

Mocking an external Library for unit test

I have a below function tryGet() to unit test:
type config struct {
Key string `json:"key"`
Client todo.Client `json:"client"`
}
var instance *config
func (c *config) tryGet() error {
client := &http.Client{}
tClient := Client{"http://url", client}
configValues := config{"Key", tClient}
Instance := &configValues
err := Instance.Client.perform("GET", header)
return nil
}
// External library in package named "todo" has the below structs and functions
package todo
type Client struct {
BaseURL string
HTTPClient *http.Client
}
func (client *Client) perform() error {
return nil
}
I am finding a hard time to mock the Client and perforn in external package todo
If the external library is not under your control, which I assume is the case, then you should assume that the code within is tested, therefore you should create the boundary at a point that you have control of the code.
To do this you should create the interface at the config struct boundary.
type ClientInterface interface {
perform() error
}
type config struct {
Url string `json:"url"`
Client ClientInterface `json:"client"`
}
var instance *config
func (c *config) tryGet() error {
err := c.Client.perform("GET", header)
return nil
}
By doing it this way, you don't care about testing the lower level code base and you just care that this module has a perform function and that given certain conditions your code behaves correctly.
You can then create a mock todo.Cient struct that you can replace the normal one with and have it return all sorts of things and behaviors to test your code.
You can mock the function as follow
type myImpl todo.Client
func (client *myImpl) perform() error {
// do what you want to assert in the test
return nil
}
And then you will use myImpl whenever you have to use todo.Client
if you are using a function with a parameter of type todo.Client, it will not work if you pass an argument of type myImpl. It will throw an error:
cannot use client (type myImpl) as type todo.Client in field value
To solve this issue, an interface can be created
type Client interface {
perform() error
}
Now the type Client should replace the type todo.Client of the function to be unit tested.
type config struct {
Url string `json:"url"`
Client Client `json:"client"`
}
With this change the above code which supply an implementation myImpl of the interface Client should work

How to write an interface for a complex http client like gorequest with methods that returns itself for chaining

I'm writing a package which needs to pass an instance of a *gorequest.SuperAgent to a method in a subpackage
// main.go
func main() {
req := gorequest.New()
result := subpackage.Method(req)
fmt.Println(result)
}
// subpackage.go
func Method(req *gorequest.SuperAgent) string {
req.Get("http://www.foo.com").Set("bar", "baz")
_, body, _ := req.End()
return body
}
I keep going in circles trying to write an interface for the gorequest superagent so I can properly isolate and test my subpackage methods with a stub of gorequest.
type Getter Interface {
Get(url string) Getter
// In the previous Method, Get() returns a *gorequest.SuperAgent
// which allows chaining of methods
// Here I tried returning the interface itself
// But I get a 'wrong type for Get method' error when passing a gorequest instance
// have Get(string) *gorequest.SuperAgent
// want Get(string) Getter
End(callback ...func(response *gorequest.Response, body string, errs []error)) (*gorequest.Response, string, []error)
// I have no idea how to handle the param and returned *gorequest.Response here
// Put another interface ?
// Tried replacing it with *http.Response but not quite understanding it
}
func Method(req Getter) string {
...
}
So as you can see I'm tripping up on several points here and haven't been able to find a good source to learn from. Any pointers would be greatly appreciated
Besides defining the Getter interface you can also define a thin wrapper around *gorequest.SuperAgent that implements the Getter interface.
type saGetter struct {
sa *gorequest.SuperAgent
}
func (g *saGetter) Get(url string) Getter {
g.sa = g.sa.Get(url)
return g
}
func (g *saGetter) Set(param string, value string) Getter {
g.sa = g.sa.Set(param, value)
return g
}
func (g *saGetter) End(callback ...func(response *gorequest.Response, body string, errs []error)) (*gorequest.Response, string, []error) {
return g.sa.End(callback...)
}
Then with your Method defined as:
// subpackage.go
func Method(req Getter) string {
req.Get("http://www.foo.com").Set("bar", "baz")
_, body, _ := req.End()
return body
}
You can use the saGetter in main like so:
// main.go
func main() {
req := gorequest.New()
result := subpackage.Method(&saGetter{req})
fmt.Println(result)
}
Then mocking Getter for testing the Method implementation is easy.
That said, I agree with #JimB's comments that you probably don't need gorequest and using net/http is generally the better choice.

Typecasting one function into another in golang

I have the following code:
package vault
type Client interface {
GetHealth() error
}
func (c DefaultClient) GetHealth () error {
resp := &VaultHealthResponse{}
err := c.get(resp, "/v1/sys/health")
if err != nil {
return err
}
return nil;
}
Now, I want to use this function as part of this struct:
type DependencyHealthFunction func() error
type Dependency struct {
Name string `json:"name"`
Required bool `json:"required"`
Healthy bool `json:"healthy"`
Error error `json:"error,omitempty"`
HealthFunction DependencyHealthFunction
}
Basically, set the value of HealthFunction to GetHealth. Now, when I do the following:
func (config *Config) GetDependencies() *health.Dependency {
vaultDependency := health.Dependency{
Name: "Vault",
Required: true,
Healthy: true,
HealthFunction: vault.Client.GetHealth,
}
temp1 := &vaultDependency
return temp1;
}
This gives me an error and it says cannot use vault.Client.GetHealth (type func(vault.Client) error) as type health.DependencyHealthFunction in field value. How can I do this?
Edit: How DependencyHealthFunction is used?
As its part of Dependency struct, it's simply used as following: d.HealthFunction() where d is a variable of type *Dependency.
This is abstract:
HealthFunction: vault.Client.GetHealth,
If we were to call HealthFunction(), what code do you expect to run? vault.Client.GetHealth is just a promise that such a function exists; it isn't a function itself. Client is just an interface.
You need to create something that conforms to Client and pass its GetHealth. For example, if you had a existing DefaultClient such as:
defaultClient := DefaultClient{}
Then you could pass its function:
HealthFunction: defaultClient.GetHealth,
Now when you later call HealthFunction() it will be the same as calling defaultClient.GetHealth().
https://play.golang.org/p/9Lw7uc0GaE
I believe the issue is related to understanding how interfaces are treated in Go.
An interface simply defines a method or set of methods that a particular type must satisfy to be considered as "implementing" the interface.
For example:
import "fmt"
type Greeter interface {
SayHello() string
}
type EnglishGreeter struct{}
// Satisfaction of SayHello method
func (eg *EnglishGreeter) SayHello() string {
return "Hello"
}
type SpanishGreeter struct{}
func (sg *SpanishGreeter) SayHello() string {
return "Ola"
}
func GreetPerson(g Greeter) {
fmt.Println(g.SayHello())
}
func main() {
eg := &EnglishGreeter{}
sg := &SpanishGreeter{}
// greet person in english
GreetPerson(eg)
// greet person in spanish
GreetPerson(sg)
}
You can add this behavior into a custom struct by simply having a Greeter field inside the struct. ie
type FrontEntrance struct {
EntranceGreeter Greeter
}
fe := &FrontEntrance { EntranceGreeter: &EnglishGreeter{} }
// then call the SayHello() method like this
fe.EntranceGreeter.SayHello()
Interfaces in golang are useful for composing common expected behavior for types based on the methods that they satisfy.
Hope this helps.

Resources