A practical tutorial about end-to-end testing with Golang

Golang Tutorial end-to-end testing

Unit tests form a crucial part of modern software testing, however, we tend to neglect end-to-end tests for various reasons. Through a case study, I’ll show you how these were added to an open-source project so that you can adapt the approach and techniques for your own work.

The testing pyramid

In “Test Driven Development by Example”, Kent Beck defines the concept of “unit tests”:

“Unit tests test individual units (modules, functions, classes) in isolation from the rest of the program.”

I spend most of my time writing unit tests because their isolated nature means they’re relatively stable, quick to run, and don’t need to change often.

Other types of tests can be useful, but they come with their own tradeoffs. They also come with advisory proportions, we can visualize that through the use of a “Testing Pyramid”

The Testing Pyramid

My take on a “Testing Pyramid”

Unit tests, at the bottom of the diagram, are easy to maintain, quick to write, and verify only one thing, they test something in isolation.

As we progress up the diagram, the tests involve more and more components of our system and even reach out to external servers and resources, finally recruiting a web browser and automation tooling like Selenium.

The perils of unit testing are a lack of context. Without testing components together, we rely purely upon assumptions about interactions.

Above: a meme of an umbrella that passes unit tests, but fails on integration testing.

At some companies I’ve worked at, UI tests may be carried out by hand, making them error-prone and extremely expensive to exercise. That said, I’ve seen firsthand how valuable it can be to have a skilled QA tester try to break your work in creative ways.

So how do we apply this to Go?

A case study with Golang testing

I started arkade to be a marketplace for Kubernetes applications and charts. It makes them easier to discover and install with a single command. I had to write the same instructions repeatedly and knew that setting up a local development environment could be made faster and more efficient through automation.

Here’s an example of what that looks like:

arkade install kubernetes-dashboard
arkade install argocd

But we are not going to talk about installing apps here.

Shortly after arkade launched, it turned out that most Kubernetes developers also needed a core set of CLIs on their machines like terraform, kubectl, kubectx, kind, k3d, kail, k9s, and the list goes on. Arkade could also help here by making any of these binaries just one command away:

arkade get [email protected] \
  faas-cli \
  k9s \
  helm \
  k3d \
  [email protected]

Arkade does something different from other toolings like apt-get or brew. It inspects your system, then downloads the correct binary for your Operating system and CPU.

Unit testing URL templates

We add a Golang template for each tool that executes and generates a download URL. How do you unit test that?

An abridged sample from tools.go to provide the k3sup binary: