Go Interface Generation
Tags: go, buffalo, domaindrivendesign
Published: 2020-02-20 23:39:20

---
title: Go Interface Generation
published: true
date: 2018-10-26 21:58:35 UTC
tags: go, buffalo, domain-driven-design
canonical_url: https://www.skillscheck.co.uk/post/go-interface-generation/
---

# Go Interface Generation

I’d like to rave about a tool that I discovered recently. But first…a little history.

I’ve been working with [Buffalo](https://github.com/gobuffalo/buffalo) and enjoying it very much. It’s all there in front of you, in Go (my very favourite language) and it’s easy to take your service from the basic out-of-the-box template to adding your own domain models and business logic.

But I also have a background in Java of the Big Business kind. We’re talking full [DDD](https://en.wikipedia.org/wiki/Domain-driven_design) and [3-tier applications](https://www.techopedia.com/definition/24306/three-tier-application) in web containers, JCA components and fancy messaging solutions.

Now the thing I like about 3-tier applications is the strong seperation of concerns. Buffalo, as easy as it is, doesn’t have this same seperation. Buffalo deals with this by making strong use of integration tests using a full database implementation and utilities to manage database schemas and populating the database with data according to what is defined in your text fixtures.

In short, I want to be able to take the business logic out of the handler and put it in a repository that I can abstract away so that when I do my testing (first), I can make sure that I’m only testing 1 thing at a time.

## How do we achieve this in Buffalo?

Well the obvious way is to move the logic for making queries against the database in to its own type, passing in the dependencies as members of this class. Stub out the dependencies - a route for validating outputs against the inputs that we insert.

Buffalo makes extensive use of the incredibly useful package [Pop](https://github.com/gobuffalo/pop). Unfortunately, access to the database in this package is via the [Connection](https://godoc.org/github.com/gobuffalo/pop#Connection) type. It’s unfortunate because it is a `struct`, which means that we can’t just insert a mock version of it in to the units that we’d like to test.

To perform this injection of a mock, we need to create an interface that exposes each of the exported functions on the `Connection` type.

After a little searching, I found [`ifacemaker`](github.com/vburenin/ifacemaker). This tools reads the struct and creates an interface that allows access to each of the exported functions on that type.

To run the tool, I did this:

```
ifacemaker --file ~/go/src/github.com/gobuffalo/pop/connection.go --struct Connection --iface DBConnection --pkg repo

```

This presented the following type:

```
package repo

// DBConnection ...
type DBConnection interface {
        String() string
        // URL returns the datasource connection string
        URL() string
        // MigrationURL returns the datasource connection string used for running the migrations
        MigrationURL() string
        // MigrationTableName returns the name of the table to track migrations
        MigrationTableName() string
        // Open creates a new datasource connection
        Open() error
        // Close destroys an active datasource connection
        Close() error
        // Transaction will start a new transaction on the connection. If the inner function
        // returns an error then the transaction will be rolled back, otherwise the transaction
        // will automatically commit at the end.
        Transaction(fn func(tx *Connection) error) error
        // NewTransaction starts a new transaction on the connection
        NewTransaction() (*Connection, error)
        // Rollback will open a new transaction and automatically rollback that transaction
        // when the inner function returns, regardless. This can be useful for tests, etc...
        Rollback(fn func(tx *Connection)) error
        // Q creates a new "empty" query for the current connection.
        Q() *Query
        // TruncateAll truncates all data from the datasource
        TruncateAll() error
}

```

We can now use this in our Go mocking tools - for example [`github.com/stretchr/testify/mock`](https://github.com/stretchr/testify/). But if we’re going to use a mock, we have to generate one. Here’s where we turn to [mockery](https://github.com/vektra/mockery/). The first time you use it - it’ll fail. This is because it doesn’t populate the package imports and package names (for example, `Connection` rather than `pop.Connection`). Fortunately this is easy to fix, giving this:

```
package repo

import (
	"github.com/gobuffalo/pop"
)

// DBConnection represents a Buffalo pop connection to a database
type DBConnection interface {
	String() string
	// URL returns the datasource connection string
	URL() string
	// MigrationURL returns the datasource connection string used for running the migrations
	MigrationURL() string
	// MigrationTableName returns the name of the table to track migrations
	MigrationTableName() string
	// Open creates a new datasource connection
	Open() error
	// Close destroys an active datasource connection
	Close() error
	// Transaction will start a new transaction on the connection. If the inner function
	// returns an error then the transaction will be rolled back, otherwise the transaction
	// will automatically commit at the end.
	Transaction(fn func(tx *pop.Connection) error) error
	// NewTransaction starts a new transaction on the connection
	NewTransaction() (*pop.Connection, error)
	// Rollback will open a new transaction and automatically rollback that transaction
	// when the inner function returns, regardless. This can be useful for tests, etc...
	Rollback(fn func(tx *pop.Connection)) error
	// Q creates a new "empty" query for the current connection.
	Q() *pop.Query
	// TruncateAll truncates all data from the datasource
	TruncateAll() error
}

```

The next time we run `mockery`, it magically works. Take a look at this snippet, if it compiles then you have yourself an interface over a `struct` to unlock your unit tests.

The following code _should_ work, although you may need to fill in the blanks:

```

func AcceptConnection(tx DBConnection) {}

func TestMocks(t *testing.T) {
    tx := &pop.Connection{}
    mx := &mocks.DBConnection{}

    AcceptConnection(tx)
    AcceptConnection(mx)
}

```

If you get this far then you have all of the components needed to add a testable repository tier in your Buffalo app.