DEV Community

Cover image for Test Driven Development & Go
Mohammad Quanit
Mohammad Quanit

Posted on • Edited on

Test Driven Development & Go

In this article I'll be discussing about Test Driven Development, its approaches, best practices in the context of Golang with some code examples. Here's the github repo link from where I am going to show you some code examples

Test Driven Development in short (TDD) is a software development approach where you write test cases for a small piece of code before writing the actual code. The goal is to ensure that the code meets specific requirements and behaves correctly in various scenarios. By writing tests first, you can catch errors early in the development process, and ensure that your code is easy to test, maintain, and refactor.

TDD is a cycle of writing a test, seeing it fail, writing code to pass the test, and then refactoring the code to improve its quality.

test driven development tdd - lifecycle


Motivations for TDD

  • Helps catch errors early: By writing tests before writing the actual code, TDD can help catch errors early in the development process. This reduces the time spent on debugging and ensures that your code is more reliable.

  • Ensures code meets requirements: TDD ensures that your code meets specific requirements and behaves correctly in various scenarios. This makes it easier to maintain and refactor your code as your project evolves.

  • Improves code quality: TDD encourages developers to write modular, testable, and maintainable code. This improves the overall quality of the codebase and reduces technical debt.

  • Reduces the cost of change: Since TDD ensures that your code is well-tested and modular, it reduces the cost of making changes to your codebase. This is particularly useful in large projects where changes can have significant impacts on the system.

  • Enables faster development: TDD can actually help speed up development by reducing the time spent on debugging and ensuring that new code doesn't break existing functionality.

  • Facilitates collaboration: TDD provides a common language and framework for collaboration between developers and other stakeholders. This helps ensure that everyone is on the same page and that the project moves forward smoothly.

  • Boosts confidence: TDD gives developers confidence in their code by ensuring that it behaves correctly and meets specific requirements. This confidence can lead to better decisions, more creativity, and more efficient development.

Testing in Golang

Golang provides a built-in testing tool that automates the process of running tests. The tool, called "go test", is easy to use and can be run from the command line. The Go testing package provides a range of functions and methods for creating and running tests. It includes functions for comparing values, reporting errors, and running tests in parallel.

The testing package also provides helper functions for reporting errors, such as "Error," "Errorf," and "Fail." These functions can be used to report errors that occur during the test.

Here's an example of a Test Case written in Go:


 golang
func TestSum(t *testing.T) {
    t.Run("Sum of numbers in array", func(t *testing.T) {
        numbers := []int{5, 4, 3, 2, 1}

        got := Sum(numbers)
        want := 16

        if got != want {
            t.Errorf("got %d, want %d, given %d", got, want, numbers)
        }
    })
}



Enter fullscreen mode Exit fullscreen mode

Points to remember when writing test case in golang.

The file name for the test case must end with _test.go i.e main_test.go so that Go tool can detect what file to execute when running go test command to run test cases.

Function for the test code, must be starts with Test keyword i.e TestSum. That's how go tool knows to run that test functions written in test files.

Go's testing tool can also generate a coverage report, which shows how much of your code is covered by tests. Command for getting test coverage is


 bash
go test -cover


Enter fullscreen mode Exit fullscreen mode

Go's testing package also includes support for benchmarks, which can be used to measure the performance of your code. Benchmark functions have a specific signature and are executed multiple times to provide an accurate measurement of performance. Command to run benchmarks on test cases.


bash
go test -bench=.
Enter fullscreen mode Exit fullscreen mode




Table Driven Testing

Go's testing package also supports table-driven tests, which allow you to test a function with multiple inputs and expected outputs. This can be useful for testing edge cases and ensuring that your code is robust.

Here's an example of Table Driven Test:


golang
func TestSum(t *testing.T) {
cases := []struct {
description string
num1 int
num2 int
expected int
}{
{
description: "1 + 2",
num1: 1,
num2: 2,
expected: 3,
},
{
description: "3 + 4",
num1: 3,
num2: 4,
expected: 7,
},
{
description: "10 + 45",
num1: 10,
num2: 45,
expected: 70,
},
}
for _, tt := range cases {
    t.Run(tt.description, func(t *testing.T) {
        result := Sum(tt.num1, tt.num2)
        if result != tt.expected {
            t.Errorf("expected %d, but got %d", tt.expected, result)
        }
    })
}
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




TDD Best Practices to follow

  1. The fundamental principle of TDD is to write tests before writing the actual code.

  2. Tests should be small and focused on a single piece of functionality. This allows you to easily pinpoint errors when they occur.

  3. Use Go testing tool to simplify and speed up your testing workflow.

  4. Table-driven tests allow you to test a function with multiple inputs and expected outputs. This can help you catch edge cases and ensure your code is robust.

  5. When writing tests that rely on external dependencies, use mocks to simulate the behaviour of those dependencies. This allows you to test your code in isolation and avoid flaky tests.

  6. Use this feedback to refactor your code and make it more modular, maintainable, and testable.

  7. Make sure your tests are updated to reflect any changes you make to your code.

  8. Code coverage tools like go test -cover can help you identify areas of your code that are not adequately covered by your tests.

  9. Use a continuous integration (CI) tool to automate your testing workflow. This ensures that your tests are run regularly and that any errors are caught early in the development process.

Tools by Go Community

Go comes with its built-in testing tool, but there are some open source tools available as well created by Go community all around the world. Here are some of them that are mostly used in Go projects:

  1. Gomega - Matcher/Assertion lib https://github.com/onsi/gomega

  2. GoCheck - Featured rich testing lib https://github.com/go-check/check

  3. Testify - Toolkit for mocks, assertions https://github.com/stretchr/testify

  4. GoMock - A dedicated mocking framework https://github.com/golang/mock

  5. Ginkgo - A BDD testing framework for expressive specs https://github.com/onsi/ginkgo

Conclusion

Test Driven Development is a mental model where you write test cases before actual production code. It required some practice if you haven't written any test case before. Either you're working on real-world projects or just starting out, you can start exploring and do hands on on it.

If you like my article, please like and share feedback and let me know in comments. And don't forget to follow me on Linkedin, Github, Twitter.

Peace ✌🏻

Top comments (2)

Collapse
 
informatik01 profile image
Levan Kekelidze

[Minor correction]

You wrote:

The file name for the test case should starts with _test.go i.e main_test.go

Seems like it should be something like this:

The file name for the test case must end with _test.go (e.g. main_test.go etc).

Collapse
 
mquanit profile image
Mohammad Quanit

Thanks Levan, for pointing this mistake & now its Updated