title: Writing unit test with mocks
body: <p>I wrote a REST api in it. Now I want to do its unit testing it by isolating different components. </p>
  
  <p>The project structure looks like below and there are two major packages</p>
  
  <pre><code>├── main.go
  ├── routes
  │   ├── routes.go
  │   └── routes_test.go
  ├── db
  │   └── db.go
  </code></pre>
  
  <p><strong>main.go</strong>: Main entry point for the project</p>
  
  <p><strong>routes/routes.go</strong>: HTTP route handler package</p>
  
  <p><strong>db/db.go</strong>: database handler package</p>
  
  <p>Now when I am testing the http routing I only want to test if the requests are routed to proper handler functions are then they respond accordingly. In real application the handler function would actually insert/update the database but I don't want to do that while testing. So if I can mock a database object and make my handler functions connect/read/write against that, but I am not sure how to do that.</p>
  
  <p>When it comes to testing the database handler package <code>db.go</code>, I think i can't mock that and I have to either set up a test db against which I should make queries.</p>
  
  <p>This is how my files look.</p>
  
  <p>routes/routes.go</p>
  
  <pre><code>    package routes
  
      import (
          "cfengine-service/db"
          "encoding/json"
          "net/http"
          "strings"
  
          log "github.com/sirupsen/logrus"
  
          "github.com/gorilla/mux"
      )
  
      // Route - struct modal for routes
      type Route struct {
          Name        string
          EndPoint    string
          Methods     []string
          HandlerFunc http.HandlerFunc
      }
      var dbHandler = &amp;db.Mongo{}
  
      // SetupRouter - returns the mux router
      func SetupRouter() *mux.Router {
          err := dbHandler.Connect()
          if err != nil {
              log.Fatal(err)
          }
          router := mux.NewRouter()
          for _, route := range SetUpRoutes() {
              router.HandleFunc(route.EndPoint, route.HandlerFunc).Methods(route.Methods...)
          }
          return router
  
      }
      func SetUpRoutes() []Route {
          return []Route{
              {
                  "Host config Management",
                  "/configs/group",
                  []string{"GET", "POST"},
                  GroupConfig,
              },
      }
  
  func GroupConfig(w http.ResponseWriter, r *http.Request) {
      reqMethod := r.Method
      if reqMethod == "GET" {
          GetGroupConfig(w, r)
      } else if reqMethod == "POST" {
          NewGroupConfig(w, r)
      }
  
  }
  
  func GetGroupConfig(w http.ResponseWriter, r *http.Request) {
      w.Header().Set("Content-Type", "application/json")
      // g := r.URL.Query().Get("groupname")
      g := r.FormValue("groupname")
      if g == "" {
          if data, err := dbHandler.ReadAllGroups(); StatusWriter(w, err) {
              _ = json.NewEncoder(w).Encode(data)
          }
      } else {
          if data, err := dbHandler.ReadOneGroup(g); StatusWriter(w, err) {
              _ = json.NewEncoder(w).Encode(data)
          }
      }
  
  }
  
  // NewGroupConfig - end point for creating new config group
  func NewGroupConfig(w http.ResponseWriter, r *http.Request) {
      w.Header().Set("content-type", "application/json")
      var group db.GroupConfig
      err := json.NewDecoder(r.Body).Decode(&amp;group)
      StatusWriter(w, err)
  
      err = dbHandler.CreateGroupConfig(&amp;group)
      StatusWriter(w, err)
  }
  </code></pre>
  
  <p><strong>db/db.go</strong></p>
  
  <pre><code>    package db
  
      import (
          "cfengine-service/config"
          "cfengine-service/helpers"
          "context"
          "fmt"
          "time"
  
          log "github.com/sirupsen/logrus"
          "go.mongodb.org/mongo-driver/bson"
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
  
      type HostConfig struct {
          Hostname  string            `bson:"_id" json:"hostname"`
          Type      string            `bson:"type" json:"type"`
          Groups    []string          `bson:"groups" json:"groups"`
          Overrides map[string]string `bson:"overrides" json:"overrides"`
          Excludes  []string          `bson:"excludes" json:"excludes"`
          Data      map[string]string `bson:"data" json:"data"`
      }
  
  
      // Mongo collection
      type Mongo struct {
          collection *mongo.Collection
      }
  
      // Storage is interface for db operations
      type Storage interface {
          Connect() error
          ReadAll() ([]HostConfig, error)
          ReadOne() (*HostConfig, error)
          CreateHostConfig(h *HostConfig) error
          CreateGroupConfig(g *GroupConfig) error
          DeleteHostConfig(h *HostConfig) error
          UpdateHostGrops(h string, g string, opr string) error
          validateGroup(ctx context.Context, val string) error
      }
  
      func (m *Mongo) Connect() error {
          ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
          defer cancel()
          //client, err := mongo.NewClient(config.Config.MongoURI)
          client, err := mongo.Connect(ctx, options.Client().ApplyURI(config.Config.MongoURI))
          if err != nil {
              return err
          }
  
          log.Info("Connected to MongoDB.....")
          m.collection = client.Database(config.Config.MongoDbName).Collection(config.Config.MongoCollectionName)
          return nil
      }
  func (m *Mongo) CreateGroupConfig(g *GroupConfig) error {
      ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
      defer cancel()
      data, err := bson.Marshal(g)
      if err != nil {
          log.Error(err.Error())
          return err
      }
      _, err = m.collection.InsertOne(ctx, data)
      if err != nil {
          return err
      }
      return nil
  }
  
  func (m *Mongo) ReadOneGroup(g string) (GroupConfig, error) {
      ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
      defer cancel()
      var group GroupConfig
      err := m.collection.FindOne(ctx, bson.M{"_id": g}).Decode(&amp;group)
      if err != nil {
          return GroupConfig{}, err
      }
  
      return group, nil
  }
  
  func (m *Mongo) ReadAllGroups() ([]GroupConfig, error) {
      var configs []GroupConfig
      ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
      defer cancel()
      cur, err := m.collection.Find(ctx, bson.M{"type": "group"})
      if err != nil {
          return nil, err
      }
      defer cur.Close(ctx)
      for cur.Next(ctx) {
          var record GroupConfig
          if err = cur.Decode(&amp;record); err != nil {
              return nil, err
          }
          configs = append(configs, record)
      }
      return configs, nil
  }
  </code></pre>
  
  <p>So using the <code>mockgen</code> package i generated the mocks for <code>Storage</code> interface in my <code>db.go</code> but I am not sure how do I start using that. Here is how mock_db.go looks.</p>
  
  <p><strong>mock_db.go</strong></p>
  
  <pre><code>type MockStorage struct {
      ctrl     *gomock.Controller
      recorder *MockStorageMockRecorder
  }
  
  // MockStorageMockRecorder is the mock recorder for MockStorage
  type MockStorageMockRecorder struct {
      mock *MockStorage
  }
  
  // NewMockStorage creates a new mock instance
  func NewMockStorage(ctrl *gomock.Controller) *MockStorage {
      mock := &amp;MockStorage{ctrl: ctrl}
      mock.recorder = &amp;MockStorageMockRecorder{mock}
      return mock
  }
  
  // EXPECT returns an object that allows the caller to indicate expected use
  func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
      return m.recorder
  }
  
  // Connect mocks base method
  func (m *MockStorage) Connect() error {
      m.ctrl.T.Helper()
      ret := m.ctrl.Call(m, "Connect")
      ret0, _ := ret[0].(error)
      return ret0
  }
  
  // Connect indicates an expected call of Connect
  func (mr *MockStorageMockRecorder) Connect() *gomock.Call {
      mr.mock.ctrl.T.Helper()
      return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockStorage)(nil).Connect))
  }
  func (m *MockStorage) CreateGroupConfig(arg0 *db.GroupConfig) error {
      m.ctrl.T.Helper()
      ret := m.ctrl.Call(m, "CreateGroupConfig", arg0)
      ret0, _ := ret[0].(error)
      return ret0
  }
  
  // CreateGroupConfig indicates an expected call of CreateGroupConfig
  func (mr *MockStorageMockRecorder) CreateGroupConfig(arg0 interface{}) *gomock.Call {
      mr.mock.ctrl.T.Helper()
      return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGroupConfig", reflect.TypeOf((*MockStorage)(nil).CreateGroupConfig), arg0)
  }
  </code></pre>
  
  <p>So can anyone please guide me here or give me some hints to move in right direction.</p>