Should I use an interface to allow for mocking? - unit-testing

I'm writing a JSON validator in Go, and I want to test another object that interacts with my Validator. I've implemented the Validator as a struct with methods. To allow me to inject a mock Validator into another object, I've added an interface, which the Validator implements. I've then swapped argument types to expect the interface.
// Validator validates JSON documents.
type Validator interface {
// Validate validates a decoded JSON document.
Validate(doc interface{}) (valid bool, err error)
// ValidateString validates a JSON string.
ValidateString(doc string) (valid bool, err error)
}
// SchemaValidator is a JSON validator fixed with a given schema.
// This effectively allows us to partially apply the gojsonschema.Validate()
// function with the schema.
type SchemaValidator struct {
// This loader defines the schema to be used.
schemaLoader gojsonschema.JSONLoader
validationError error
}
// Validate validates the given document against the schema.
func (val *SchemaValidator) Validate(doc interface{}) (valid bool, err error) {
documentLoader := gojsonschema.NewGoLoader(doc)
return val.validate(documentLoader)
}
// ValidateString validates the given string document against the schema.
func (val *SchemaValidator) ValidateString(doc string) (valid bool, err error) {
documentLoader := gojsonschema.NewStringLoader(doc)
return val.validate(documentLoader)
}
One of my mocks looks like this:
// PassingValidator passes for everything.
type PassingValidator bool
// Validate passes. Always
func (val *PassingValidator) Validate(doc interface{}) (valid bool, err error) {
return true, nil
}
// ValidateString passes. Always
func (val *PassingValidator) ValidateString(doc string) (valid bool, err error) {
return true, nil
}
This works, but it doesn't feel quite right. Collaborators won't see anything other than my concrete type in production code; I've only introduced the interface to suit the test. If I do this everywhere, I feel like I'll be repeating myself by writing interfaces for methods that will only ever have one real implementation.
Is there a better way to do this?

Update: I retract my previous answer. Do not export interfaces across packages. Make your funcs return the concrete type, so to allow the consumer to create their own interface and overrides if they wish.
See: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
HatTip: #rocketspacer
I also typically code my Tests in a different package than my package code. That way, I can only see what I export (and you sometimes see what you are cluttering up if exporting too much).
Following this guideline, for testing your package, the process would be:
Create your complex object as normal with funcs
Use interfaces internally as needed (for example, Car <- Object -> House)
Only export your concretes, not interfaces
During testing, specify a test method that takes a Test interface of your concrete methods, and change up your interface as needed. you create this test interface in your test package.
Original Answer Below for Posterity
Export only your interface, not your concrete type. And add a New() constructor so people can instantiate a default instance from your package, that conforms to the interface.
package validator
type Validator interface {
Validate(doc interface{}) (valid bool, err error)
ValidateString(doc string) (valid bool, err error)
}
func New() Validator {
return &validator{}
}
type validator struct {
schemaLoader gojsonschema.JSONLoader
validationError error
}
func (v *validator) Validate(doc interface{}) (valid bool, err error) {
...
}
func (v *validator) ValidateString(doc string) (valid bool, err error) {
...
}
This keeps your API package clean, with only Validator and New() exported.
Your consumers only need to know about the interface.
package main
import "foo.com/bar/validator"
func main() {
v := validator.New()
valid, err := v.Validate(...)
...
}
This leaves it up to your consumers to follow dependency injection patterns and instantiate (call the New()) outside of its usage, and inject the instance where ever they use it. This would allow them to mock the interface in their tests and inject the mock.
Or, the consumer could care less and just write the main code above which is short and sweet and gets the job done.

IMHO this is a good solution. Interfaces give you more freedom when testing and re-implementing. I use interfaces often and I never regret it (especially when testing), even when it was a single implementation (which is, in my case, most of the time).
You may be interested in this: http://relistan.com/writing-testable-apps-in-go/

Related

Mocking os.GetEnv("ENV_VAR")

I am trying to mock the Go function os.GetEnv() in my test files so that I can get the desired value for a particular environment variable.
For example I have defined.
abc := os.GetEnv("XYZ_URL")
Here I should be able to get the needed value for the variable abc. Also I have several places with the GetEnv functions.
It will be really helpful if someone can give me a workaround without the help of any Go framework.
First, you can't mock that function. You can only mock something exposed as an interface.
Second, you probably don't need to. Mocks, broadly speaking, are over-used, and should be avoided whenever possible.
When testing environment variables, you have few options.
If you're using Go 1.17 or newer, you can take advantage of the new Setenv function, which sets the environment variable for the duration of the current test only:
func TestFoo(t *testing.T) {
t.Setenv("XYZ_URL", "http://example.com")
/* do your tests here */
}
For older versions of Go, consider these options:
Create an object which can be mocked/doubled, which exposes the necessary functionality. Example:
type OS interface {
Getenv(string) string
}
type defaultOS struct{}
func (defaultOS) Getenv(key string) string {
return os.Getenv(key)
}
// Then in your code, replace `os.Getenv()` with:
myos := defaultOS{}
value := myos.Getenv("XYZ_URL")
And in your tests, create a custom implementation that satisfies the interface, but provides the values you need for testing.
This approach is useful for some things (like wrapping the time package), but is probably a bad approach for os.Getenv.
Make your functions not depend on os.Getenv, and instead just pass the value in. Example, instead of:
func connect() (*DB, error) {
db, err := sql.Connect(os.Getenv("XYZ_URL"), ...)
/* ... */
return db, err
}
use:
func connect(url string) (*DB, error) {
db, err := sql.Connect(url, ...)
/* ... */
return db, err
}
In a sense, this only "moves" the problem--you may still want to test the caller, which uses os.Getenv(), but you can at least reduce the surface area of your API that depends on this method, which makes the third approach easier.
Set the environment expliclitly during your tests. Example:
func TestFoo(t *testing.T) {
orig := os.Getenv("XYZ_URL")
os.Setenv("XYZ_URL", "http://example.com")
t.Cleanup(func() { os.Setenv("XYZ_URL", orig) })
/* do your tests here */
}
This approach does have limitations. In particular, it won't work to run multiple of these tests in parallel, so you still want to minimize the number of these tests you run.
This means that approaches 2 and 3 in conjunction with each other can be very powerful.
A final option is to create a function variable, that can be replaced in tests. I've talked about this in another post, but for your example it could look like this:
var getenv = os.Getenv
/* ... then in your code ... */
func foo() {
value := getenv("XYZ_URL") // Instead of calling os.Getenv directly
}
and in a test:
func TestFoo(t *testing.T) {
getenv = func(string) string { return "http://example.com/" }
/* ... your actual tests ... */
}
This has many of the same limitations as option #3, in that you cannot run multiple tests in parallel, as they will conflict.

Golang mock a function with dynamic function calls

I'm pretty new to Go and still learning about how things work in Go, so with that said I've been looking in to Go testing approach and how mocking would work for the past few weeks and most of the information I found based on functions being concrete.
E.g. everything is a function is either passed as a receiver or a parameter, however, the problem I face is my function uses a switch case to determine what function it should be called, so it's not passed from outside.
func (n *Notification) Notify(m Message) error {
switch n.Service {
case "slack":
var s slack.Slack
s.User = m.User
s.Host = m.Host
s.Provider = m.Provider
s.SystemUser = m.SystemUser
return s.SlackSend(n.Url)
default:
return errors.New(codes.CODE5)
}
}
Above code is what the function I want to test looks like and I'm having a hard time figuring out how I could mock the SlackSend() function.
I've come across some article say I should write the function in the test file and when I'm happy with what function do, I should write the real code. This doesn't sound right to me because for me it feels like I have the same code in two places and test only uses the one in test and I could change the real code and break it without testes detecting that.
I mainly work on Python and I'm used to using things like Mock/MagicMock that can intercept the function call and replace on at runtime, so apologies in advance if I don't quite get the Go's TDD approach.
This is what test code would look like if anyone wonders:
type MockSlack struct {
*slack.Slack
}
func (s *MockSlack) SlackSend(url string) error {
if url != "" {
return nil
} else {
return errors.New("url empty")
}
}
func TestNotify(t *testing.T) {
m := Message{}
n := Notification{
Service: "slack",
Url: "https://dummy.io",
}
if err := n.Notify(m); err != nil {
t.Errorf("SlackSend, expected: %s, got: %s", "nil", err.Error())
}
}
Obviously the MockSlack structure is not taking effect because it's not really passed in anywhere.
If anyone has any advice on how I could mock this or what I should do instead would be much appreciated.
Thanks in advance.
UPDATE:
Background
This isn't a web server/applicaition of a sort. It's an SSH authentication plugin so it would be a server-side application. As for Notify method, it server's the purpose of a mapper. So it can call Slack, MS Teams, AWS SNS which give caller less conditions to process and how and where the notification it sent is decided by the Notify method.
If you are not able to change the Notify Method to make it testing friendly. One option would be to consider using monkeypatching. Here is an example provided for *net.Dialer:
func main() {
var d *net.Dialer // Has to be a pointer to because `Dial` has a pointer receiver
monkey.PatchInstanceMethod(reflect.TypeOf(d), "Dial", func(_ *net.Dialer, _, _ string) (net.Conn, error) {
return nil, fmt.Errorf("no dialing allowed")
})
_, err := http.Get("http://google.com")
fmt.Println(err) // Get http://google.com: no dialing allowed
}
WARNING: It's not safe to use it outside of a testing environment.

How to mock http.Head()

I'm studying the outyet example project from https://github.com/golang/example/tree/master/outyet. The test file does not cover the case where http.Head(url) returns an error. I would like to extend the unit tests to cover the if statement where the error is logged (https://github.com/golang/example/blob/master/outyet/main.go#L100). I would like to mock http.Head(), but I'm not sure how to do this. How can this be done?
The http.Head function simply calls the Head method on the default HTTP client (exposed as http.DefaultClient). By replacing the default client within your test, you can change the behaviour of these standard library functions.
In particular, you will want a client that sets a custom transport (any object implementing the http.RoundTripper interface). Something like the following:
type testTransport struct{}
func (t testTransport) RoundTrip(request *http.Request) (*http.Response, error) {
# Check expectations on request, and return an appropriate response
}
...
savedClient := http.DefaultClient
http.DefaultClient = &http.Client{
Transport: testTransport{},
}
# perform tests that call http.Head, http.Get, etc
http.DefaultClient = savedClient
You could also use this technique to mock network errors by returning an error from your transport rather than an HTTP response.

Execute test cases in a pre-defined order

Is there a way to execute test cases in GoLang in a pre-defined order.
P.S: I am writing test cases for life cycle of a event. So I have different api's for all the CURD operations. I want to run these test cases in a particular order as only if an event is created it can be destroyed.
Also can I get some value from one test case and pass it as input to another. (example:- To test the delete event api, I need a event_id which i get when I call create_event test case)
I am new to GoLang, can someone please guide me through.
Thanks in advance
The only way to do it is to encapsulate all your tests into one test function, that calls sub-functions in the right order and with the right context, and pass the testing.T pointer to each so they can fail. The down-side is that they will all appear as one test. But in fact that is the case - tests are stateless as far as the testing framework is concerned, and each function is a separate test case.
Note that although the tests may run in the order they are written in, I found no documentation stating that this is actually a contract of some sort. So even though you can write them in order and keep the state as external global variables - that's not recommended.
The only flexibility the framework gives you since go 1.4 is the TestMain method that lets you run before/after steps, or setup/teardown:
func TestMain(m *testing.M) {
if err := setUp(); err != nil {
panic(err)
}
rc := m.Run()
tearDown()
os.Exit(rc)
}
But that won't give you what you want. The only way to do that safely is to do something like:
// this is the whole stateful sequence of tests - to the testing framework it's just one case
func TestWrapper(t *testing.T) {
// let's say you pass context as some containing struct
ctx := new(context)
test1(t, ctx)
test2(t, ctx)
...
}
// this holds context between methods
type context struct {
eventId string
}
func test1(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
c.eventId = "something"
}
func test2(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
doSomethingWith(c.eventId)
}

Interface use in golang for mocking third party libraries

I'm trying to create a simple mock for unit testing some code using the VMware vSphere API client - govmomi - but I'm having trouble finding a usable pattern.
A simple use case for the client library would be to retrieve the installed licenses for a vSphere cluster:
vclient, err := govmomi.NewClient(*vcurl, true)
if err != nil {
return err
}
lic, err := vclient.LicenseManager().ListLicenses()
NewClient() returns a pointer to a Client structure, Client.LicenseManager() returns an instance of a LicenseManager structure, and LicenseManager.ListLicenses() returns a slice of structures containing the license info. Coming from a Python background, I'd usually monkey patch the ListLicenses() method on LicenseManger for a mock, but I can't seem to come up with a comparable pattern or methodology in Go.
To this point, I've tried creating a wrapper structure VCenterClient with the govmomi Client structure as an anonymous member and a "constructor" function NewVCenter() to create new instances of the wrapper structure with logic for mocks:
import (
"net/url"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/vim25/types"
)
type VCenterClient struct {
VCenterClientInterface
}
type VCenterClientInterface interface {
LicenseManager() LicenseManager
}
type LicenseManager interface {
ListLicenses() ([]types.LicenseManagerLicenseInfo, error)
}
type VCenterClientMock struct{}
type LicenseManagerMock struct{}
func (v *VCenterClientMock) LicenseManager() LicenseManager {
return LicenseManagerMock{}
}
func (l LicenseManagerMock) ListLicenses() ([]types.LicenseManagerLicenseInfo, error) {
return make([]types.LicenseManagerLicenseInfo, 0), nil
}
func NewVCenterClient(uri string, mock bool) *VCenterClient {
if mock {
return &VCenterClient{&VCenterClientMock{}}
}
vcurl, _ := url.Parse(uri)
vclient, _ := govmomi.NewClient(*vcurl, true)
return &VCenterClient{vclient}
}
...but I having trouble using interfaces to properly abstract the nested structures in the govmomi library. I know the above will not work as govmomi.LicenseManager() returns a structure of type govmomi.LicenseManager and my VCenterClientInterface.LicenseManager() method returns an interface of type LicenseManager. However, I'm struggling to find an alternative.
Any help on a better design pattern or proper use of interfaces in this case would be much appreciated.
This library is a SOAP client (http://godoc.org/github.com/vmware/govmomi/vim25/soap#Client). Abstract at the HTTP layer with net/http/httptest (http://golang.org/pkg/net/http/httptest/) or by using your own HTTPRoundtripper to mock the response.