TutorialsLast Updated Feb 5, 20245 min read

Continuous integration for Go applications

Fikayo Adepoju

Fullstack Developer and Tech Author

Developer C sits at a desk working on a beginner-level project.

Go, an open-source programming language backed by Google, makes it easy to build simple, reliable, and efficient software. Go’s efficiency with network servers and its friendly syntax make it a useful alternative to Node.js. All network applications need well-tested features, and those developed in Go are no different. In this tutorial, we will be building and testing a simple Go blog.

Prerequisites

To follow this tutorial, a few things are required:

  1. Basic knowledge of programming
  2. Go installed on your system (you can find an installation guide here.
  3. A CircleCI account
  4. A GitHub. account

Our tutorials are platform-agnostic, but use CircleCI as an example. If you don’t have a CircleCI account, sign up for a free one here.

Once all these requirements are met, you can begin the tutorial.

Setting up a simple Go blog project

To set up a Go project, create the project folder. Go to the root of the folder and enter:

mkdir ci-go-application
cd ci-go-application

Next, use the go mod command to initialize the project at the root:

go mod init go-testing

This will initialize the project with the package name go-testing. You can use any package name you want, as long as it follows standard package naming conventions. .

Now, create a main.go file for the entry point of the application:


package main

import "fmt"

func main(){
  fmt.Println("Good to Go!")
}

This code prints the string Good to Go! to the CLI interface. Run this code by running this command on the CLI:

go run main.go

Creating the blog model

In this step, you will create a model (in object-oriented programming terms) for an example blog. This model creates a new instance of a blog and adds articles to it.

At the root of the project, create a new Go file named blog.go. Paste this into it:


package main

type Article struct {
	Title string `json:"title"`
	Body  string `json:"body"`
}

type Blog struct {
	Articles []Article
}

func New() *Blog {
	return &Blog{}
}

func (b *Blog) SaveArticle(article Article) {
	b.Articles = append(b.Articles, article)
}

func (b *Blog) FetchAll() []Article {
	return b.Articles
}

This code contains two data types and three functions that work like this:

  • The Article struct type contains the properties Title and Body. It uses json bindings to title and body respectively.
  • Blog is a struct type representing the blog itself. It consists of an array of Article data types.
  • New() is a method that instantiates a new Blog. In other words, it returns a new instance of Blog.
  • The SaveArticle method adds a new article to a blog’s article collection.
  • FetchAll is a method that retrieves, in an array, all Article data types in a Blog instance.

To test the blog model you just created, replace everything in main.go with this:


package main

import (
	"fmt"
)

func main() {

	blog := New()

	fmt.Println(blog)

	blog.SaveArticle(Article{"My first Blog post", "Today, we will be talking about blogging"})

	fmt.Println(blog)

}

This code creates a new blog instance printed on stdout. The SaveArticle method saves the new article instance and the blog is once again printed to stdout. Now that you have more than one Go file in the project, use this command to run the two files:

go run blog.go main.go

The blog instance is printed out on your CLI.

Blog test - CLI

The first printout of the blog instance is empty. The second printout takes place after we added an article, and so it contains that article.

Adding tests to the Go project

Time to add some tests. You will be adding test scripts that assert that the SaveArticle and FetchAll methods work as expected.

Create a test file, blog_test.go, at the root of the project and paste this into it:


package main

import "testing"

func TestSaveArticle(t *testing.T) {

	blog := New()

	blog.SaveArticle(Article{"My title", "My Post Body"})

	if blog.Articles[0].Title != "My title" {
		t.Errorf("Item was not added")
	}
}

func TestFetchAllArticles(t *testing.T) {

	blog := New()

	blog.SaveArticle(Article{"My title", "My Post Body"})

	articles := blog.FetchAll()

	if len(articles) == 0 {
		t.Errorf("Fetch All fails")
	}
}

In this code, the testing package (which can be found in Go’s standard library) is imported. Two functions are then written to test the SaveArticle and FetchAll methods respectively.

TestSaveArticle creates a new Blog instance and saves an Article. It then checks the title to make sure the saved article is contained in the blog. If there is no saved article, an error causes the test to fail.

TestFetchAllArticles creates a new Blog instance and saves an Article. Then it calls FetchAll to retrieve the articles and checks that the blog contains any article. If there is no article, an error indicates that FetchAll failed to return the new article.

Running tests locally

Run these tests using the verbose flag -v to get more information. Enter this:

go test -v

Once the command finishes running, you will get a message on your CLI.

Local Test - CLI

This screenshot shows that the two tests ran successfully and passed testing. Adding the -v flag displays specific feedback about how each test ran instead of just the usual summary.

Automating testing with CircleCI

Your next task is to automate the testing process with continuous integration (CI) so tests run when code is pushed to a remote repository. Setting up test automation with CircleCI consists of three steps:

  1. Adding the CircleCI pipeline configuration to automate the testing process
  2. Creating a remote repository and pushing the project code there
  3. Adding the repository as a project on CircleCI

Start by creating the build pipeline configuration file to contain your configuration script.

At the root of the project, create a folder named .circleci and a config.yml file within it. Inside config.yml enter the following configuration:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: cimg/go:1.15.10
    steps:
      - checkout
      - restore_cache:
          keys:
            - go-mod-v4-{{ checksum "go.sum" }}
      - run:
          name: Install Dependencies
          command: go get ./...
      - save_cache:
          key: go-mod-v4-{{ checksum "go.sum" }}
          paths:
            - "/go/pkg/mod"
      - run:
          name: Run tests
          command: go test -v

This script pulls in the CircleCI Docker image for Go as the testing environment. The project code is then checked out of the remote repository for the project. After dependencies are installed and cached, the test script (go test -v) runs.

Commit the changes to your project and then push your project to GitHub.

Next, go to the Add Projects page on the CircleCI dashboard.

Add Project - CircleCI

Click Set Up Project to begin.

Add Config - CircleCI

On the Set Up Project page, click Fastest to instruct CircleCI that you will be using the config file on your branch. Then click Set Up Project to finish. The build should be executed successfully.

Build successful - CircleCI

You can click into the build to view the step-by-step details of the deployment.

Conclusion

Go provides a testing suite out of the box, as you have learned in this tutorial. Using Go can make it easier to practice test-driven development (TDD) by optimizing benefits and reducing workload costs. That gives you and your team more time to develop features for your applications.

The complete source code can be found here on GitHub.

Happy coding!


Fikayo Adepoju is a LinkedIn Learning (Lynda.com) Author, Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs, and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.

Copy to clipboard