Continuous integration for Go applications
Fullstack Developer and Tech Author
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:
- Basic knowledge of programming
- Go installed on your system (you can find an installation guide here.
- A CircleCI account
- 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 propertiesTitle
andBody
. It usesjson
bindings totitle
andbody
respectively. Blog
is astruct
type representing the blog itself. It consists of an array ofArticle
data types.New()
is a method that instantiates a newBlog
. In other words, it returns a new instance ofBlog
.- The
SaveArticle
method adds a new article to a blog’s article collection. FetchAll
is a method that retrieves, in an array, allArticle
data types in aBlog
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.
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.
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:
- Adding the CircleCI pipeline configuration to automate the testing process
- Creating a remote repository and pushing the project code there
- 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.
Click Set Up Project to begin.
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.
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!