How to do it...

These steps cover writing and running your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter9/mockingand navigate to this directory.
  2. Run the following command:
$ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter9/mocking

You should see a file called go.mod that contains the following:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter9/mocking    
  1. Create a file called mock.go with the following content:
        package mocking

// DoStuffer is a simple interface
type DoStuffer interface {
DoStuff(input string) error
}
  1. Create a file called patch.go with the following content:
        package mocking

import "reflect"

// Restorer holds a function that can be used
// to restore some previous state.
type Restorer func()

// Restore restores some previous state.
func (r Restorer) Restore() {
r()
}

// Patch sets the value pointed to by the given destination to
// the given value, and returns a function to restore it to its
// original value. The value must be assignable to the element
//type of the destination.
func Patch(dest, value interface{}) Restorer {
destv := reflect.ValueOf(dest).Elem()
oldv := reflect.New(destv.Type()).Elem()
oldv.Set(destv)
valuev := reflect.ValueOf(value)
if !valuev.IsValid() {
// This isn't quite right when the destination type is
// not nilable, but it's better than the complex
// alternative.
valuev = reflect.Zero(destv.Type())
}
destv.Set(valuev)
return func() {
destv.Set(oldv)
}
}
  1. Create a file called exec.go with the following content:
        package mocking
import "errors"
var ThrowError = func() error {
return errors.New("always fails")
}

func DoSomeStuff(d DoStuffer) error {

if err := d.DoStuff("test"); err != nil {
return err
}

if err := ThrowError(); err != nil {
return err
}

return nil
}
  1. Create a file called mock_test.go with the following content:
        package mocking
type MockDoStuffer struct {
// closure to assist with mocking
MockDoStuff func(input string) error
}
func (m *MockDoStuffer) DoStuff(input string) error {
if m.MockDoStuff != nil {
return m.MockDoStuff(input)
}
// if we don't mock, return a common case
return nil
}
  1. Create a file called exec_test.go with the following content:
        package mocking
import (
"errors"
"testing"
)

func TestDoSomeStuff(t *testing.T) {
tests := []struct {
name string
DoStuff error
ThrowError error
wantErr bool
}{
{"base-case", nil, nil, false},
{"DoStuff error", errors.New("failed"), nil, true},
{"ThrowError error", nil, errors.New("failed"), true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// An example of mocking an interface
// with our mock struct
d := MockDoStuffer{}
d.MockDoStuff = func(string) error {
return tt.DoStuff }

// mocking a function that is declared as a variable
// will not work for func A(),
// must be var A = func()
defer Patch(&ThrowError, func() error { return
tt.ThrowError }).Restore()

if err := DoSomeStuff(&d); (err != nil) != tt.wantErr
{
t.Errorf("DoSomeStuff() error = %v,
wantErr %v", err, tt.wantErr)
}
})
}
}
  1. Fill in tests for the remaining functions and go up one directory and run go test. Ensure that all the tests pass:
$go test
PASS
ok github.com/PacktPublishing/Go-Programming-Cookbook-Second-
Edition/chapter9/mocking 0.006s
  1. The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.