How to partially implement a Golang interface for testing

5 min read

Go Gopher with Unit Testing, Mocks and interfaces.

When writing tests for your code which make use of interfaces, Golang can be quite fiddly to work with. You may only wish to mock one method but given the nature of interfaces you'll often implement them all to satisfy the compiler. What if there were a way to create partial interfaces Golang would accept?

What is a Go interface?

At its core, a Go interface is simply a collection of method signatures. It defines a set of methods that a type must implement in order to be considered an instance of that interface. For example, consider the following interface definition:

type UserDatabase interface { GetUser() string InsertUser() error }

This defines an interface called UserDatabase with two method signatures GetUser and InsertUser. Any type that has a method with these exact signature is considered to be an implementation of the UserDatabase interface.

How do interfaces work in Go?

In Go, a type is said to implement an interface if it defines all the methods in the interface. This is determined at compile-time, which means that any code that uses an interface can be sure that the methods defined in the interface will be available at runtime.

For example, let's say we have a type UserStore that implements the UserDatabase interface:

type UserStore struct {} func (m *UserStore) GetUser() string { return "Mark" }

Because UserStore has a method with the signature GetUser(), it is considered to be an implementation of the UserDatabase interface. We can then use a value of type UserStore wherever a GetUser is expected:

func UserDoSomething(u UserDatabase) { u.GetUser() } func main() { var u UserStore UserDoSomething(u) }

In this example, we define a function called UserDoSomething that takes a value of type UserDatabase. We then create an instance of UserStore and pass it to UserDoSomething. Because UserStore implements the UserDatabase interface, it can be used as an argument to UserDoSomething.

This is the power of Go interfaces - they allow for code to be written in a way that is independent of the specific types being used. By defining interfaces that describe the behavior that is required, we can write functions and other code that can work with any type that satisfies those requirements.

Want to be the first to hear about Go jobs?

Sign up to the Work in Golang weekly digest:

Partially implementing an interface in Golang

So you've been tasked with writing tests for a method which implements an interface, and you want to stub the output of that method without implementing the entire interface. How do you do that, and why would you want to do that?

Let's say your website makes use of an interface for it's user store/database as we've been discussing in the above examples. You might not wish to spin up a test database, but instead mock the output of a GetUser() method which implements the UserDatabase interface. In this case, you can do the following:

type UserDatabase interface { GetUser() string InsertUser() error } type UserStore struct { // Database sql.Database // Other bits you might want in a UserStore object } func NewUserStore() *UserStore { return &UserStore{} } func (m *UserStore) GetUser() string { // sql.Database.Execute()..... return "Mark" } func (m *UserStore) InsertUser() error { return nil } // Embed and promote the UserStore methods to MockUserDatabase type MockUserDatabase struct { UserStore MockUser string } func (m *MockUserDatabase) GetUser() string { return m.MockUser } func main() { // In your actual code u := NewUserStore() fmt.Println(u.GetUser()) // In your tests m := MockUserDatabase{ MockUser: "Not Mark!", } fmt.Println(m.GetUser()) } // Code Output: Mark // Test Output: Not Mark!

Have a play in the Go Playground.

In the above code we define an interface called UserDatabase that specifies two methods: GetUser() that returns a string and InsertUser() that returns an error. We also have a method called NewUserStore() which returns a UserStore struct which is used by GetUser() and InsertUser().

We then have the MockUserDatabase struct which embeds and promotes the UserStore methods from the UserDatabase interface, however here we are partially defining the interface with just the GetUser method. As you can see, we're returning the mocked value of m.MockUser as the response of this method, completely bypassing any of the database logic you may have had to deal with in your testing. As a result, we have two strings which are printed: One which prints Mark, and one which prints Not Mark! which we've "injected" via a partially implemented interface mock. Now when writing tests, you'll be able to use a for loop to iterate over all your test cases and make uses of these new mocked values to test against different criteria.

In summary

By making use of partial interfaces Golang allows you to easily mock what would otherwise be complicated methods which might call out to databases, RPCs, and external services. You've now greatly simplified your testing strategy, and cut back the lines of code required to run valid and complex integration tests. Interfaces are a fairly straight forward concept, however Go's implicit use of them can make for a bit of a confusing time. Hopefully armed with this knowledge, you'll be able to tackle your testing problems with great ease.