模拟外部库以进行单元测试

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

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

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.