Mahdi.Kh
September 21, 2026
In this part, we get familiar with unit testing in Go and learn how to write tests for our own code.
Unit testing is the most basic kind of test for checking that code works correctly. If we have a pyramid for the different kinds of tests, unit testing sits at the bottom of that pyramid.
As we can see, unit testing sits at the lowest level of the pyramid, meaning the first kind of test you should usually write is a unit test. The pyramid's cross-section represents the amount of testing. The bottom of the pyramid is wider than its top, and it gets narrower and smaller as you go up. In most cases, the amount of unit testing is greater than all the other kinds of testing combined.
In Go, you don't need to install separate tools to write and run unit tests; using just Go's standard packages, you can write your tests and then run them with the Go command line.
Suppose we have code for summing integers, as follows, and this code is in the sum.go file:
In Go, test files have names with the _test suffix. So if our code is in the sum.go file, our tests will go in a file named sum_test.go. Every test is a function whose name starts with Test and that has a parameter of type *testing.T.
For the code above, we can write our test inside the sum_test.go file, as follows:
To run every test, we can use the go test ./… command, and to run a specific test, we can specify the test's name using -run; for example, here we can use go test -run ^TestSum$ ./..., where TestSum represents the name of the test we want.
By passing ./…, we specify that Go should search the current directory and all its subdirectories for tests, and run them if it finds any.
Running go test -run ^TestSum$ ./... runs our test, and if it passes, it shows a result like this:
We can add more tests. For example, let's have two tests: one for the case where we pass a single number to , and one for the case where we give this function several numbers as input:
sumInside *testing.T, we can use the following methods to report errors:
Log: writes the given parameters to the output.Logf: like Log, writes the given parameters to the output, but it lets us use text formatting when writing to the output.Errorf: by calling the Logf method, writes the information we want to the output, and then calls the Fail method.Error: by calling Log, writes the information to the output, and then calls the Fail method.Fail: marks the function running the test as failed. Test execution continues.FailNow: marks the current function running the test as failed and immediately ends execution of the remaining statements (it runs os.Exit, so no statement after FailNow runs, not even deferred statements).Fatal: calls Log and then FailNow. Works like Error, except test execution doesn't continue and stops immediately.Fatalf: calls Logf and then FailNow. Works like Errorf, except test execution doesn't continue and stops immediately.The T type has more useful functions, which you can see in the standard package.
A table test, or table-driven test, is a standard pattern where, instead of writing repetitive functions for each test case, we define the input data and expected output as an array or slice, and then simply loop over that array's elements and check the output.
The code above can be rewritten using the table-driven approach, as follows:
If we have a lot of table test cases (more than two or three, say), it's hard to find out which one didn't pass if they fail. In these cases, using t.Run, we can run each test case as a subtest, so the output shows exactly which test ran into a problem.
We can rewrite the code above as follows:
Running the test above runs each subtest separately, and its result is shown in the output:
To see each test's result separately, you need to use the -v flag.
To run tests in parallel, you can call the t.Parallel() method at the start of each test. This way, the tests will run in parallel, and the tests' running time can decrease significantly.
The code above can be rewritten as follows:
Note: when tests run in parallel, their order might be different each time you run them.
You can also see this part's code on GitHub.
Previous part: Context | Golang basics
Next part: The time Standard Package | Golang basics