Go Interfaces Make Development and Testing Easier

Go Interfaces Make Development and Testing Easier

Substitutability or the Liskov Substitution Principle (LSP) is a concept that I’ve tried to adhere to for some years when writing code. It’s beneficial for many reasons, but particularly when testing, as it can indirectly force you to write code that is more testable. Recently, I’ve started appreciating how it works in Go, and will step through how in this short article.


If you’re not familiar with the term, it was created by Barbara Liskov in 1987 and is effectively the following:

An object (such as a class) may be replaced by a sub-object (such as a class that extends the first class) without breaking the program.

In modern PHP, the principle is pretty trivial to implement. For example, when writing functions, instead of specifying a function parameter as a concrete type or a specific class, you specify an interface instead. Then, so long as the parameter passed to the function implements said interface, then the function’s signature is satisfied.

Take the example below, where you can see what I mean.

<?php

interface SomeInterface {
    public function someFunction(string $paramOne, string $paramTwo): string;
}

class SomeClass
{
    public function someFunction(SomeInterface $param): string
    {
        return "some string";
    }
}

The example above defines an interface named SomeInterface which has a method named someFunction(). Then, it defines a class with a single function, named someFunction(), which takes a SomeInterface object. By doing this so long as someFunction() is passed an object that implements SomeInterface, then the compiler won’t throw an error.

This approach makes the code easier to test, because you create a mock object with the applicable methods and pass it to the function during testing. This avoids the need to properly instantiate a concrete object, one which may have a series of dependencies, such as a database connection which could be expensive and time-intensive to set up.

Gladly, substitutability is even easier (or more concise) in Go. Take the following code example.

type Shortener interface {
	Shorten() string
}

type App struct {
	db        *sql.DB
	shortener Shortener
}

type SpyURLShortener struct {

}

func (s *SpyURLShortener) Shorten() string {
	return "short"
}

app := App{db: db, shortener: shortener}

The code starts off by defining an interface named Shortener which defines a single method named Shorten() which returns a string. Then, it defines a custom type, a struct named App. App has two properties:

  • db: a pointer to an sql.DB object
  • shortener: any object that implements the Shortener interface.

After that, a custom type named SpyURLShortener is defined. This type implements a method named Shorten() which returns a string. Because it it defines the Shorten method and the method has the same signature as that defined by the Shortener interface, according to the Go compiler the type implements that the Shortener interface.

Because of this, no explicit keyword is required to tell the compiler that a type implements a given interface, as the compiler can infer that information for itself. So long as the type follows these two rules, then it implements a given interface.

That’s why Go interfaces make development and testing easier

To me, this is a more concise way of implementing the Liskov Substitution Principle than I’ve seen in other C-based languages (such as PHP and Java).

But, it does take some getting used to. What’s more, at least initially, I feel that it increases cognitive load. Given that, I still appreciate the explicitness of those languages, from time to time, when developing with Go. By having an extends or implements keyword means that there’s no mistaking an object’s type or inheritance hierarchy.

However, Go’s implicit approach does afford it greater flexibility. Said another way, it’s an excellent example of duck-typing, or:

If it walks like a duck and it quacks like a duck, then it must be a duck.

There’s more to Go interfaces…

If you’d like to dive deeper into Go Interfaces, then have a read on Go’s empty interface.


You might also be interested in these tutorials too...

Understanding Go's Empty Interface
Tue, Sep 12, 2023

Understanding Go's Empty Interface

Recently, while working with the Twilio Lookup API, I had the opportunity to properly learn about Go’s empty interface. During the process, I realised that I didn’t understand it properly, so took the opportunity to do so. Today, I am going to step through what it is and how simple they are to work with.

Restrict Allowed Route Methods in Go Web Apps
Fri, Sep 1, 2023

Restrict Allowed Route Methods in Go Web Apps

It’s incredibly easy to build web apps and APIs using Go’s net/http package. All you really need to do is to specify a route and its handler. But, how do you restrict the methods that routes can accept? In this short tutorial, I’ll show you how.

Live Reload Go Projects with wgo
Fri, Apr 19, 2024

Live Reload Go Projects with wgo

Building web apps in Go is extremely rewarding. However, as Go’s a compiled language, to see changes, you need to restart the app. Needless to say that’s quite tedious! With live reloading, it doesn’t need to be.

Restrict HTTP Request Methods in Go 1.22
Thu, Apr 11, 2024

Restrict HTTP Request Methods in Go 1.22

Restricting HTTP request methods, when working with net/http prior to Go 1.22 was a little complicated. However, that’s all changed. Now, it’s pretty trivial. In this short tutorial, I’ll show you the new approach.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus