- Published on
Unit Testing in Go
- Authors
- Name
In this blog post, we'll explore how to write unit tests in Go, one of the most popular programming languages for building fast and efficient applications.
Setting Up Your Go Project
Before we dive into writing unit tests, let's set up a basic Go project. Here is the structure of our project:
├── main.go
├── main_test.go
├── go.mod
└── go.sum
Writing Your First Unit Test
Let's start by creating a simple function in main.go
that we'll test.
package main
import "fmt"
func Add(a, b int) int {
return a + b
}
func main() {
fmt.Println("Hello, World!")
}
Now, let's write a test for the Add
function in main_test.go
.
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("expected %d, got %d", expected, result)
}
}
Running Your Tests
To run your tests, you can use the go test
command:
go test
If everything is set up correctly, you should see an output indicating that the test passed.
PASS
ok example.com/myapp 0.001s
Testing Multiple Cases
It's often useful to test multiple cases with different inputs and expected outputs. You can do this by creating a table-driven test.
func TestAddMultipleCases(t *testing.T) {
tests := []struct {
a, b int
expected int
}{
{1, 1, 2},
{2, 2, 4},
{2, 3, 5},
{5, 5, 10},
}
for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d): expected %d, got %d", tt.a, tt.b, tt.expected, result)
}
}
}
Testing for Errors
Sometimes, you want to test functions that return errors. Let's modify our Add
function to return an error if the inputs are negative.
package main
import (
"errors"
"fmt"
)
func Add(a, b int) (int, error) {
if a < 0 || b < 0 {
return 0, errors.New("inputs must be non-negative")
}
return a + b, nil
}
func main() {
fmt.Println("Hello, World!")
}
And the corresponding test:
func TestAddWithError(t *testing.T) {
result, err := Add(-1, 1)
if err == nil {
t.Errorf("expected an error but got none")
}
if result != 0 {
t.Errorf("expected result to be 0 but got %d", result)
}
}
Mocking Dependencies
When writing unit tests, you might need to mock dependencies such as databases or external services. Go doesn't have a built-in mocking framework, but you can create your own mocks using interfaces.
Let's say we have a UserService
that depends on a UserRepository
interface.
type UserRepository interface {
GetUser(id int) (string, error)
}
type UserService struct {
repo UserRepository
}
func (s *UserService) GetUserName(id int) (string, error) {
return s.repo.GetUser(id)
}
We can create a mock implementation of UserRepository
for testing.
type MockUserRepository struct{}
func (m *MockUserRepository) GetUser(id int) (string, error) {
if id == 1 {
return "Alice", nil
}
return "", errors.New("user not found")
}
func TestGetUserName(t *testing.T) {
mockRepo := &MockUserRepository{}
service := &UserService{repo: mockRepo}
name, err := service.GetUserName(1)
if err != nil {
t.Errorf("expected no error but got %v", err)
}
if name != "Alice" {
t.Errorf("expected 'Alice' but got %s", name)
}
}
Conclusion
Unit testing is a crucial part of developing robust Go applications. By writing tests for your code, you can ensure that your application behaves as expected and catches bugs early in the development process. We covered the basics of writing and running unit tests in Go, as well as testing multiple cases, handling errors, and mocking dependencies. Happy testing!