mardi 27 septembre 2016

Mocking functions in Golang to test my http routes

I'm totally confused figuring out how I can mock a function, without using any additional packages like golang/mock. I'm just trying to learn how to do so but can't find many decent online resources.

Essentially, I followed this excellent article that explains how to use an interface to mock things.

As so, I've re-written the function I wanted to test. The function just inserts some data into datastore. My tests for that are ok - I can mock the function directly.

The issue I'm having is mocking it 'within' an http route I'm trying to test. Am using the Gin framework.

My router (simplified) looks like this:

func SetupRouter() *gin.Engine {

    r := gin.Default()
    r.Use(gin.Logger())
    r.Use(gin.Recovery())

    v1 := r.Group("v1")
    v1.PATCH("operations/:id", controllers.UpdateOperation)
}

Which calls the UpdateOperation function:

func UpdateOperation(c *gin.Context) {
    id := c.Param("id")
    r := m.Response{}

    str := m.OperationInfoer{}
    err := m.FindAndCompleteOperation(str, id, r.Report)

    if err == nil {
      c.JSON(200, gin.H{
          "message": "Operation completed",
      })
    }
}

So, I need to mock the FindAndCompleteOperation() function.

The main (simplified) functions looks like this:

func (oi OperationInfoer) FindAndCompleteOp(id string, report Report) error {
    ctx := context.Background()
    q := datastore.NewQuery("Operation").
        Filter("Unique_Id =", id).
        Limit(1)

    var ops []Operation

    if ts, err := db.Datastore.GetAll(ctx, q, &ops); err == nil {
        {
            if len(ops) > 0 {
                ops[0].Id = ts[0].ID()
                ops[0].Complete = true

                // Do stuff

                _, err := db.Datastore.Put(ctx, key, &o)
                if err == nil {
                  log.Print("OPERATION COMPLETED")
                }
            }
        }
    }

    err := errors.New("Not found")
    return err
}

func FindAndCompleteOperation(ri OperationInfoer, id string, report Report) error {
    return ri.FindAndCompleteOp(id, report)
}

type OperationInfoer struct{}

To test the route that updates the operation, I have something like so:

FIt("Return 200, updates operation", func() {
    testRouter := SetupRouter()

    param := make(url.Values)
    param["access_token"] = []string{public_token}

    report := m.Report{}
    report.Success = true
    report.Output = "my output"

    jsonStr, _ := json.Marshal(report)
    req, _ := http.NewRequest("PATCH", "/v1/operations/123?"+param.Encode(), bytes.NewBuffer(jsonStr))

    resp := httptest.NewRecorder()
    testRouter.ServeHTTP(resp, req)

    Expect(resp.Code).To(Equal(200))

    o := FakeResponse{}
    json.NewDecoder(resp.Body).Decode(&o)
    Expect(o.Message).To(Equal("Operation completed"))
})

Originally, I tried to cheat a bit and just tried something like this:

m.FindAndCompleteOperation = func(string, m.Report) error {
  return nil
}

But that affects all the other tests etc.

I'm hoping someone can explain simply what the best way to mock the FindAndCompleteOperation function so I can test the routes, without relying on datastore etc.

Aucun commentaire:

Enregistrer un commentaire