Using mock on Golang tests
Tags: go, tests
Published: 2019-01-02 12:43:43

---
title: Using mock on Golang tests
published: true
description: learn how to write tests with mock using golang
tags: golang, tests
---

I was studying golang these days and needed to write some tests.
I'm new at golang and i was not familiar with testing and stuffs like that so it was a little "hard" to me to get into it, this post is to make this easy for you as I wish it had been easy for me, i'll cover the basic principles to write tests with sql mock using the main golang structure (interfaces) to keep it simple.

> I'm assuming that you're already familiar with golang

## Golang interfaces
If you come from a language like `java`, `PHP` or any other OOP language you must be familiar with `interfaces`, in short an interface is a `contract` used to guarantee that a specific class implements determined method/attributes.
`Go` has interfaces too and is very simple to write it:

```go
type repository interface {}
```

I'll use as example the `Error` interface that are built-in in `go`, as we know `errors` in `go` are a type or like some people say a first class citizen.
we can return a error, create a new type of error, pass error as parameter, store a error in a variable and so on...[checkout the error interface](https://golang.org/pkg/builtin/#error)
as you can see the `Error` interface only requires a single method called Error that returns a string, so if we do;

```go
type MyCustomError struct {
    message string
}

func (e MyCustomError) Error() string {
    return e.message
}
```

we can use `MyCustomError` as an error type.

```go

func myMethod() error {
    return MyCustomError{message: "my custom error"}
}
```

you see that our `myMethod` declares that the return of this method is a built-in error type and the method return a `MyCustomError`, this is possible because `MyCustomError` implements all methods required by the `Error` interface this way we can say that `MyCustomError` is a error type.

This is the main principle to write mocks in `go`, we create a custom struct that implementes the needed methods that provide all methods of a specific resource.

We'll not write [an entire struct for `sql.DB`](https://golang.org/src/database/sql/sql.go?s=9508:11278#L339), thanks to ~~God~~ `DATA-DOG` we already have [the package to do this job](https://github.com/DATA-DOG/go-sqlmock)


## Hands on
> In the follow examples i'll let some `TODOS` in the code so you can see what is missing, i let you a challenge at the end, good luck.

Ok, know that we know a little about interfaces/structs let's dive in `mocks`, first create a new project with these files: `database.go` and `test_database.go`

the `database.go` file:

```go
package database

import "database/sql"

type Repository interface {
    Find(id int) error
}

type repository struct {
    // the db field is of type `DB` from the `database` package
    // that provides the method to interact with our real database
    db *sql.DB
}

func NewRepository(db *sql.DB) Repository {
    return &repository{db: db}
}

// Find retrieves a single record from database
// based on id column
func (r repository) Find(id int) error {}
```

**Notes**:
The `NewRepository` method declares that will return a `Repository` interface and accept the `sql.DB` struct as parameter, this interface is only for learning propose, we do not have to write it in this example.


Let's start with the `Find` method, in your `database_test` file write the follow code:

```go
package database

import (
    "gopkg.in/DATA-DOG/go-sqlmock.v1"
    "testing"
)

func TestFindDatabaseRecord(t *testing.T) {
    // the db satisfy the sql.DB struct
    db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
	}
	defer db.Close()

    // our sql.DB must exec the follow query
    mock.ExpectQuery("SELECT \\* FROM my_table").
        // the number 3 must be on the query like "where id = 3"
        WithArgs(3)
        // TODO: define the values that need to be returned from the database

    myDB := NewRepository(db) // passes the mock to our code

    // run the code with the database mock
    if err = myDB.Find(3); err != nil {
        t.Errorf("something went wrong: %s", err.Error())
    }
}
```

**Resume**:
The code above create a mock of `sql.DB` struct and passes it as parameter to our code, the mock says that the query `SELECT * FROM my_table` must be executed with the argument `3`, the `DATA-DOG` package provides a lot of methods like `ExpectExec`, `WillReturnResult` and `ExpectRollback`, the package is smart enough to know when you run a specific method and will compare your expectations if needed.

the `Find` method:

```go

func (r repository) Find(id int) error {
    // r is the reference to our struct and db is the mock that we passed
    stmt, err := r.db.Prepare("SELECT * from my_table WHERE id = $1")
    if err != nil {
        return err
    }
    defer stmt.Close()

    // change the placeholder $1 to use the id parameter
    _, err = stmt.Exec(id) // should be result, err = stmt.Exec(id)
    if err != nil {
        return err
    }

    // TODO: we need to fill a struct with the database result
    // and return it with nil

    return nil // should be mystruct, nil
}
```

You have noted that the `Find` method doesn't return any database information, the examples of this code is very generic with the purpose of you implement it, can you handle this?

What you have to todo from here:
1. return the data on the `Find` method along with the error
2. compare the returned values

**hints**:
- take a look at the [method `NewRows`](https://godoc.org/github.com/DATA-DOG/go-sqlmock#NewRows) and the [`WillReturnRows`](https://godoc.org/github.com/DATA-DOG/go-sqlmock#ExpectedQuery.WillReturnRows).
- create a struct to hold the database values and return this struct on `Find` method

when are finished share with me what you have done! :)


Thank you for reading till here, if you have any advice, questions or suggestion i'll be glad to talk about it.