Pablo Carvalho Vieira Bello

August 25, 2024

Getting Started with Testing in Go: Reversing a String

Hey! In this blog post, we'll guide you through the basics of writing and running tests in Go. We'll take a simple function that reverses a string and walk through writing tests for it, running the tests to identify issues, and then fixing the implementation until all tests pass. Additionally, we'll demonstrate how to structure your tests using t.Run to organize multiple test cases efficiently.


Step 1: Set Up Your Project
First, let's create a new package for our string utilities
mkdir stringutils
cd stringutils

Initialise a Go module for your project:
go mod init stringutils

We'll be using the testify package for assertions in our tests. Install it with:
go get github.com/stretchr/testify

Step 2: Write Your First Test

Create a test file where we will write our first test:
touch stringutils_test.go

Now, let's write a test for the Reverse function:
// stringutils_test.go
package stringutil

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestReverse(t *testing.T) {
    // Test case: Reverse a simple string
    input := "hello"
    expected := "olleh"
    
    // Call the function to test
    result := Reverse(input)
    
    // Assert the result using testify's assert package
    assert.Equal(t, expected, result, "they should be equal")
}

Make sure your project is up to date with required dependencies:
go mod tidy

Step 3: Run the Test and Make It Fail

Run the test with:
go test

You should see an error like this:
# stringutils [stringutils.test]
./stringutils_test.go:15:12: undefined: Reverse
FAIL	stringutils [build failed]

The error indicates that the Reverse function is undefined. This is expected because we haven't written the function yet.

Step 4: Create the Reverse Function

Let's create the stringutils.go file and add a basic implementation of the Reverse function to fix the first error:
touch stringutils.go

Add the following code:
package stringutil

// Reverse returns the given string reversed.
func Reverse(s string) string {
    return ""
}

Now, when you run the test again, you should see a different error:
go test

The output should look something like this:
--- FAIL: TestReverse (0.00s)
    stringutils_test.go:18:
        	Error Trace:	/workspace/go/stringutils/stringutils_test.go:18
        	Error:      	Not equal:
        	            	expected: "olleh"
        	            	actual  : ""
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-olleh
        	            	+
        	Test:       	TestReverse
        	Messages:   	they should be equal
FAIL
exit status 1
FAIL	stringutils	0.265s

The test is failing because the Reverse function is returning an empty string, not the expected reversed string.

Step 5: Implement the Reverse Function

Let's implement the Reverse function properly:
package stringutil

// Reverse returns the given string reversed.
func Reverse(s string) string {
    reversedStr := ""

    // Loop through the string backwards
    for i := len(s) - 1; i >= 0; i-- {
        // Append each character to the reversed string
        reversedStr += string(s[i])
    }

    return reversedStr
}

Run the test again:
go test

You should see a success message:
PASS
ok  	stringutils	0.263s

Step 6: Add More Tests with t.Run()

Let's add more tests using t.Run() to structure them as sub-tests:
// stringutils_test.go
package stringutil

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestReverse(t *testing.T) {
    t.Run("Simple string", func(t *testing.T) {
        input := "hello"
        expected := "olleh"
        
        result := Reverse(input)
        assert.Equal(t, expected, result, "they should be equal")
    })

    t.Run("String with spaces and punctuation", func(t *testing.T) {
        input := "Go, Gophers!"
        expected := "!srehpoG ,oG"
        
        result := Reverse(input)
        assert.Equal(t, expected, result, "they should be equal")
    })

    t.Run("Empty string", func(t *testing.T) {
        input := ""
        expected := ""
        
        result := Reverse(input)
        assert.Equal(t, expected, result, "they should be equal")
    })
}

Run the tests again:
go test

You should see all tests passing:
PASS
ok  	stringutils	3.055s

Conclusion

In this post, we've walked through setting up a simple Go project, writing a test-first function using TDD, and structuring tests using t.Run(). This should give you a solid foundation to start testing in Go and applying TDD practices in your future projects. Happy coding!