
Golang is a statically typed language that compiles to machine code, making it a popular choice for building high-performance applications.
Golang's test framework, built into the Go standard library, provides a simple and efficient way to write unit tests for your code.
Golang's test framework is based on the xunit style, where each test is a separate function that starts with the "Test" prefix.
Writing tests first, also known as Test-Driven Development (TDD), is a best practice in Golang that helps ensure your code is correct and maintainable.
The Go standard library provides a variety of built-in functions for testing, including the "testing" package and the "reflect" package.
Golang's testing framework is designed to be easy to use and integrate with your existing code, making it a great choice for developers of all levels.
Intriguing read: Create a Package in Golang
Getting Started
Golang is a statically typed language, which means the compiler checks the types of variables at compile time. This helps catch type-related errors early on.
To write a test in Golang, you'll need to use the built-in testing package. This package provides functions like Test and Benchmark, which are used to write unit tests and benchmark functions.
Golang's testing package is designed to be simple and easy to use, making it a great choice for beginners.
Intriguing read: Golang Os.writefile
Writing Tests
Writing tests is an essential part of ensuring the quality of your code. You can write one test function per case, but this leads to a lot of duplication.
The table-driven approach is a better way to go, as it helps reduce repetition. This involves organizing a test case as a table that contains the inputs and the desired outputs.
You can use a table-driven test function for the Fooer function, which starts by defining the input structure, also known as the columns of the table. Each row of the table lists a test case to execute.
The execution loop calls t.Run(), which defines a subtest. As a result, each row of the table defines a subtest named [NameOfTheFunction]/[NameOfTheSubTest].
Curious to learn more? Check out: Golang Function Type
GoLand can generate those test templates for you. You can right-click your function and go to Generate | Test for function.
Here are the benefits of table-driven tests:
- Table tests reuse the same assertion logic, keeping your test DRY.
- Table tests make it easy to know what is covered by a test, as you can easily see what inputs have been selected.
To streamline and expand your testing capabilities, you can use table-driven testing. This approach uses a test array containing various input values and their expected results, allowing you to test multiple scenarios within a single function.
Assertions and Validations
Writing assertions manually can be tedious, especially when you have many assertions in your code.
Manually writing the validation error message is a chore, and 3 lines of code can quickly become verbose.
The solution is to use a library like stretchr/testify, which provides simple assertions with output that makes sense.
Broaden your view: Golang Source Code
Assertions
Writing assertions by hand can be a tedious task, especially when you have to manually write the validation error message. This can quickly become overwhelming when you have many assertions in your code.
Three lines of code can get verbose quickly, which is why it's better to use a library that simplifies the process.
Manually writing assertions yourself can lead to unnecessary complexity, especially when validating complex structs. This is where a library like stretchr/testify comes in handy.
Using a library like stretchr/testify gives you simple assertions with output that makes sense, making it easier to write and debug your code.
Here are some benefits of using a library like stretchr/testify:
- Less verbose code
- Simplified validation error messages
This can save you time and effort in the long run, making your development process more efficient.
Golden File Validations
Golden File Validations are a useful way to assert that a response remains the same. This can be especially helpful when dealing with big response blobs from APIs.
You can write the correct response body to a file, known as a golden file. Michell Hashimoto's approach uses a smart way of exposing command line flags to Go tests.
The goldie package makes it easier to handle command line flag setting and golden file writing and comparison transparently. It's a convenient tool for this type of testing.
To update your golden file, you'd run a command with a specific flag. This allows you to maintain the most up-to-date version of your response data.
You can run your tests as usual, without any special flags. The goldie package takes care of the rest.
A unique perspective: Install Golang Package
Return Only
In Go, unit tests are created by following a specific convention. You need to create a file with a name that ends with _test.go, and it's best practice to name the file following the file it's testing.
For example, if you have a file called math.go, the unit test file should be named math_test.go, and it's also best practice to put the test file and the implementation in the same package.
To write a unit test, you need to create a function in the test file and start the name with Test. The function should take *testing.T from the GoLang testing package, and the test function signature is TestXXX(t *testing.T) with XXX as the name of the function you are testing.
Here are some best practices to structure your tests and test cases:
- Structure your test as test tables with many test cases
- Properly assert the function behavior
- Name test cases as descriptive as possible
To run the GoLang unit test, you can use the go test terminal command, which comes from within GoLang itself. You can run all tests within the entire code base using go test ./...
A fresh viewpoint: Golang Go
Testing and Coverage
Testing and coverage are crucial aspects of writing good code in Go. You can use the go test command to generate a test report for the package you're testing by adding the -cover flag.
This flag calculates the percentage of statements covered through tests, giving you an idea of how much of your code is covered. By default, it only considers packages with test files in the coverage calculation, but you can use the -coverpkg flag to include all packages.
For example, you can use go test -coverpkg to include all packages in the coverage calculation. This is useful when you have a large codebase and want to get a comprehensive view of your code coverage.
You can also use the -coverprofile flag to create a local coverage report file, which is useful when running tests in CI/CD pipelines. This report can then be sent to your favorite code quality tool.
Consider reading: Go Template Html
There are different ways to calculate code coverage, including statement coverage, count coverage, and atomic coverage. The default mode is statement coverage, but you can use the -covermode flag to change this.
Here are the different coverage modes:
- set: Coverage is based on statements.
- count: Count is how many times a statement was run.
- atomic: Similar to count, but for parallel tests.
Knowing which flag to use is important when running tests in your CI/CD pipeline, as locally you can rely on GoLand for a friendly coverage report.
Testing Improvements
Unit testing is a crucial part of software development, and it can greatly improve software quality.
By verifying code behavior at the function/method level, unit testing helps ensure that individual units of code are working as expected.
If every unit is working properly, proven by its test cases, the overall software behavior should also work as intended.
Unit testing gives us more confidence in refactoring our code, as long as all unit tests pass, we have confidence that the behavior of the code is still the same and should work fine.
Properly defined unit test cases that cover all conditions and edge cases are essential for this confidence to be valid.
You might enjoy: Golang Use Cases
Error Handling and Logging
Error handling is an essential part of writing tests in Go, and the testing.T type provides various tools to help with this.
The t.Errorf() function prints out an error message and sets the test as failed, but it doesn't stop the test execution.
t.Error* functions don't stop the test execution, but instead, all encountered errors will be reported once the test is completed.
t.Fatal* functions are used to fail the execution of the test, and once one of these is called, the test will terminate.
Using the Log*() function can be handy to print information during the test execution, but be aware that it won't stop the test execution.
Writing out flags every time you run your tests can be tedious, but GoLand allows you to save run/debug configurations for each test, making it easier to manage your test runs.
Explore further: Run Golang File
Running and Skipping Tests
Running tests in Go is a breeze, thanks to the built-in testing support through the go test command. This command works in two distinct modes: local directory mode and package mode.
Suggestion: Golang vs Go
In local directory mode, Go compiles and executes all test files in your current working directory. You can run tests in the current package by executing go test . The -v flag enables verbose mode, providing detailed insights about each test function, including their status and execution duration.
To reduce duplication, you may want to use table-driven tests when using Parallel(), as shown in the example of running parallel tests. This can help you avoid duplicating assertion logic.
To skip tests, you can use the Skip() method, as illustrated in the example of skipping tests. This allows you to separate unit tests from integration tests and execute unit tests only when needed.
For more insights, see: Golang Mode
Running
Running tests in Go can be done in two distinct modes: local directory mode and package mode. In local directory mode, Go compiles and executes all test files in your current working directory.
To run tests in local directory mode, you can simply execute `go test` in the root directory of your project. This will compile and execute all test files in your current working directory.
Take a look at this: Golang Run Debug Mode

The expected output should resemble a list of test results, showing the status and execution duration of each test function.
Here are some examples of how to run tests in package mode:
- go test . to test the current package.
- go test ./... to test the current package and all its sub-packages.
- go test net/http to test the net/http package.
You can also enable verbose mode by adding the `-v` flag, which provides detailed insights about each test function, including their status and execution duration. This can be especially helpful for identifying test performance and failures.
Skipping
Skipping tests can be useful when you want to execute unit tests only, as they are usually faster to run than integration tests. You can use the Skip() method to separate unit tests from integration tests.
Go test accepts a flag called -test.short that allows you to run a "fast" test, but it's up to you to decide whether a test is "short" or not. You can use a combination of testing.Short() and t.Skip() to achieve this.
The testing.Short() function is set to true when the -short flag is used, and you can use t.Skip() to skip a test if it's not in short mode. This means that a test will be executed if you run go test -v, but will be skipped if you run go test -v -test.short.
Related reading: T Golang
Teardown and Cleanup
Teardown and Cleanup is an essential part of writing robust and reliable tests in Go.
The Cleanup() method is a convenient way to manage test teardown, and it's executed at the end of each test, including subtests.
Using the defer keyword can make test logic more complicated, and clutter the test function when many components are involved.
The Cleanup() function makes it clear to anyone reading the test what the intended behavior is, which is a big plus.
The Helper() method exists to improve the logs when a test fails, and it ignores the line number of the helper function, only reporting the line number of the failing test.
This helps figure out which test failed, which can be a real time-saver.
TempDir() is a method that automatically creates a temporary directory for your test and deletes the folder when the test is completed, removing the need to write additional cleanup logic.
Many Go developers are unaware of this function's existence, but it's a game-changer for test maintenance.
Here's an interesting read: Golang Line
Mocking and Dependencies
In unit testing, we only test our code in isolation, so if a function or method uses a dependency on other code, it's better to mock that dependency since our focus is to test the function or method.
Mocking in Go is different from Java, where a mock can be created using a library like Mockito. In Go, a mock can only be implemented to an interface, and it's not actually a mock, but rather an implementation of the interface for mocking purposes.
To test the UserService, we can create a mock implementation of the UserDao interface.
We can generate mock code using libraries like Mockery, which is what Halodoc uses. This generated mocking code should also be put on our codebase's Git repo because it's part of our codebase.
Here are some key facts about mocking in Go:
- Mocking in Go can only be implemented to an interface.
- A mock in Go is not actually a mock, but rather an implementation of the interface for mocking purposes.
- Libraries like Mockery can be used to generate mock code.
Frequently Asked Questions
What is the best test library for Golang?
For Golang, Testify is a top choice due to its extensive features and pre-loaded assertion functions and mock objects. It simplifies debugging and makes testing more efficient.
Featured Images: pexels.com

