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
Related
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, ...)
}
I'm trying to write a function getTargetServer() to return a polymorphic type that has both a data member URL and a method Close(). This would be a generalization of the *Server returned from httptest.NewServer() but I want to alternatively be able to return a custom type for which Close() is a NOP.
type externalTestServer struct {
URL string
}
func (externalTestServer) Close() {}
func getTargetServer() *externalTestServer {
if urlbase, ok := optionals["urlbase"].(string); ok {
return &externalTestServer{URL: urlbase}
} else {
testServer := httptest.NewServer(newMyServerHandler())
// return testServer // ### Error ###
return &externalTestServer{URL: testServer.URL}
}
}
func Test_health_check(t *testing.T) {
testServer := getTargetServer()
defer testServer.Close()
response, err := http.Get(testServer.URL + "/health")
assert.NilError(t, err)
assert.Assert(t, cmp.Equal(response.StatusCode, http.StatusOK))
}
This works like a charm except that Close() is always a NOP. When I uncomment the indicated ### Error ### line in order to return a closable *Server, I get the following error message:
cannot use testServer (type *httptest.Server) as type *externalTestServer in return argument
I understand the error, but haven't discovered a solution that lets me return a polymorphic type that generalizes *Server
Note: "A Tour of Go" defines an interface type as follows:
An interface type is defined as a set of method signatures.
Therefore, returning a simple interface will not allow for directly-accessible data members.
You could create a struct that has a field which is a string URL and a field that is a Close func. The Close func can be implemented by either externalTestServer or httptest.Server:
type server struct {
URL string
Close func()
}
if urlbase, ok := optionals["urlbase"].(string); ok {
extServer := &externalTestServer{URL: urlbase}
return &server{
URL: urlbase,
Close: extServer.Close,
}
}
testServer := httptest.NewServer(newMyServerHandler())
return &server{
URL: testServer.URL,
Close: testServer.Close,
}
http.Server is a struct, so you cannot return a polymorphic object that generalizes that. You can do something else though:
type Server interface {
GetURL() string
Close() error
}
type testServer struct {
URL string
}
func (t testServer) Close() error {}
func (t testServer) GetURL() string {return t.URL}
type httpServer struct {
*http.Server
}
func (t httpServer) GetURL() string { return the url }
You can then return Server from your function.
Chris Drew's approach is ideal for this specific case, because it requires minimal code overhead. Put another way, it is the simplest thing that will solve today's problem. The solution uses the magic of an implicit closure (for the receiver) to reference the implementation of Close() polymorphically.
My slightly simplified version of that is here...
type genericServer struct {
URL string // snapshot of internal data
Close func() // single-function polymorphism without 'interface'
}
func getTargetServer() genericServer {
if urlbase, ok := optionals["urlbase"].(string); ok {
return genericServer{URL: urlbase, Close: func() {}}
}
testServer := httptest.NewServer(newMyServerHandler())
return genericServer{URL: testServer.URL, Close: testServer.Close}
}
By embedding an actual interface type into to return struct, this concept can be seamlessly extended to better support non-trivial polymorphic interfaces. In this case, the Polymorphism is explicit based on the internal use of an interface type. Still, the returned type is a wrapper that includes a copy of the (constant) member data, so the usage is identical -- effectively generalizing that of *Server for this use-case.
type Server interface {
Close()
}
type nopServer struct {
}
func (nopServer) Close() {}
type genericServer struct {
URL string // snapshot of specific data
Server // embedded interface type for explicit polymorphism
}
func getTargetServer() genericServer {
if urlbase, ok := optionals["urlbase"].(string); ok {
return genericServer{URL: urlbase, Server: nopServer{}}
}
testServer := httptest.NewServer(newMyServerHandler())
return genericServer{URL: testServer.URL, Server: testServer}
}
Note that the URL value is not a live member of the implementation, so these solutions, as presented, are only meaningful when the data value will not change, although perhaps that limitation could be overcome by using a pointer.
I am using Google wire to wire up the dependencies in one of my Go projects. So far all the dependencies were wired up successfully.
I have following container:
type Container struct {
Logger logger.StructuredLogger
IDGenerator idgenerator.IDGenerator
Arango arangodb.Arango
}
Here StructuredLogger, IDGenerator and Arango are interface.
The container set is defined as below:
var mockContainerSet = wire.NewSet(
Container{},
ResolveStructuredLogger,
ResolveIDGenerator,
ResolveArangoDB,
)
Functions:
func ResolveStructuredLogger() logger.StructuredLogger {
panic(wire.Build(wire.InterfaceValue(new(logger.StructuredLogger), logrusprovider.NewLogrusLogger(nil))))
}
func ResolveIDGenerator() idgenerator.IDGenerator {
panic(wire.Build(wire.InterfaceValue(new(idgenerator.IDGenerator), examples.NewKSUIDGenerator())))
}
Now the issue is when I want to create a provider for Arango. The factory method for Arango provider is taking IDGenerator as an argument.
func NewMockedArango(generator idgenerator.IDGenerator) (*MockedArango, error){
if generator == nil {
return nil , errors.New("mock arangodb expects a valid IDGenerator")
}
return &MockedArango{generator:generator}, nil
}
All the functions from arangodb.Arango is implemented. And I tried the following to create a provider:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockdb.NewMockedArango(idGenerator))))
}
But the wire is throwing errors, I tried a lot of ways and no success to set this up. Any help?
Error:
2-valued mockdb.NewMockedArango(idGenerator) (value of type
(*github.com/tejashwi/catalog-manager-svc/pkg/webservice/arangodb/mockdb.MockedArango,
error)) where single value is expected
Signature of wire.Interface is
InterfaceValue(typ interface{}, x interface{})
Here you need to pass two single-valued arguments. In the second argument, you are passing 2-valued argument since function mockdb.NewMockedArango is returning 2 values.
You can change function ResolveMockArangoDB to:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
mockedArango, err := mockdb.NewMockedArango(idGenerator)
<HANDLE err>
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockedArango)))
}
I was trying to understand the Interface embedding with the following code.
I have the following:
type MyprojectV1alpha1Interface interface {
RESTClient() rest.Interface
SamplesGetter
}
// SamplesGetter has a method to return a SampleInterface.
// A group's client should implement this interface.
type SamplesGetter interface {
Samples(namespace string) SampleInterface
}
// SampleInterface has methods to work with Sample resources.
type SampleInterface interface {
Create(*v1alpha1.Sample) (*v1alpha1.Sample, error)
Update(*v1alpha1.Sample) (*v1alpha1.Sample, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.Sample, error)
List(opts v1.ListOptions) (*v1alpha1.SampleList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Sample, err error)
SampleExpansion
}
Now if I have the follwoing:
func returninterface() MyprojectV1alpha1Interface {
//does something and returns me MyprojectV1alpha1Interface
}
temp := returninterface()
Now, from the MyprojectV1alpha1Interface if I want to call the
Create function of SampleInterface
what I need to do?
Also, please explain me how this interfaces work in Golang.
In this definition:
type MyprojectV1alpha1Interface interface {
RESTClient() rest.Interface
SamplesGetter
}
Your MyprojectV1alpha1Interface embeds the SamplesGetter interface.
Embedding an interface inside another interface means all of the methods of the embedded interface (SamplesGetter) can be invoked over the embedding interface (MyprojectV1alpha1Interface).
That means you can invoke any of the SamplesGetter methods on any object that implements MyprojectV1alpha1Interface.
So once you get a MyprojectV1alpha1Interface object in your temp variable, you can call the Samples method (with suitable namespace, which I cannot guess from the code you posted):
sampleInt := temp.Samples("namespace here")
sampleInt will then have a SampleInterface object, so you can then invoke the Create function using your sampleInt variable:
sample, err := sampleInt.Create(<you should use a *v1alpha1.Sample here>)
For more details about how interfaces work, I'd suggest you go to the official specs and examples:
https://golang.org/ref/spec#Interface_types
https://gobyexample.com/interfaces
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.