A perspective on Test Driven Development (TDD) with Golang

A perspective on Test Driven Development (TDD) with Golang

— The why, and how to get started with Test Driven Development (TDD)

Test-driven development (TDD) is a software development process that involves writing tests before writing the code that satisfies those tests. This approach helps ensure the code is correct and meets requirements, making catching bugs early in the development process easier. TDD is a powerful tool that can improve the quality of your code and make it easier to maintain and evolve.

Developers either don't know about this practice or feel it is a burden to write them.

Some of the root causes for this are -

  • People often forget to run tests.

  • They write too many tests at once.

  • Write tests that are too large.

  • Write overly trivial tests.

Such habits generally do not let the developers follow excellent coding and testing practices.

TDD is a popular method for writing high-quality, maintainable code in any programming language. Go has a built-in testing framework that makes writing tests for your code manageable. The framework provides functions for writing test cases, running tests, and checking the results of those tests. Writing tests in Go is straightforward and intuitive, making it easy to get started with TDD, even if you have no prior experience with testing.

It isn’t as complicated as it seems to be. Without prior knowledge, you can start by importing the testing library and are ready to go. Define a test function and add the supporting test cases.

package main

import (
   "testing"
)

// TestUser tests CRUD of User
func TestUser(t *testing.T) {
    t.Run("Create a user successfully", func(t *testing.T) {
    }
}

You can assert the actual output with the expected output. It includes the response code, response error, response body, response header, and many more. TDD gives you the power to control and visualize the working of any API or functionality. Before writing the actual code, you can write the test and ensure its working criteria.

For example, the mock payload for the request body of the createUser API can be defined and passed on.

payload := CreateUserBody{
    Name:        "Varun Sharma",
    Description: "Insightful Software Engineer",
    Address:     "C5 Kothrud, Pune, Maharashtra",
}

We know the expected output for this request body and can assert this data with the response.

assert.Equal(t, http.StatusOK, code) 
assert.Equal(t, resp.Name, “Varun Sharma”) 
assert.Equal(t, resp.Description, “Insightful Software Engineer”) assert.Equal(t, resp.Address, “C5 Kothrud, Pune, Maharashtra”)

TDD is used not only for passing test cases but also for failing cases. You can add validation for the same API in the test. For example, I can add a valid test case where emoticons are not allowed in the user name. When I write this code, I will add validation for this test scenario. If I forget to do so by mistake, the test will fail, and I can recall this scenario.

Here is a complete sample of a test scenario.

package main

import (
    "testing"   
    "github.com/stretchr/testify/assert"
)

// TestUser tests CRUD of Entity
func TestUser(t *testing.T) {
    authHeader, mockServer, clientTimeout := GetDataForAPICall(t)
    payload := CreateUserBody{
        Name:        "Varun Sharma",
        Description: "Insightful Software Engineer",
        Address:     "C5 Kothrud, Pune, Maharashtra",
    }

    t.Run("Create User", func(t *testing.T) {
        t.Run("failed: invalid name", func(t *testing.T) {
            payload.Name = "Varun Sharma 😀"
            resp, code, _, err := makeCall( // Wrapper func to make curl over mockServer
                mockServer.URL, "/organization/user",
                http.MethodPost, payload, authHeader, clientTimeout,
            )


            assert.Equal(t, nil, err) // Ensures No error in api call
            assert.Equal(t, http.StatusUnprocessableEntity, code) // 422 Response Code
            assert.Equal(t, resp, `{“error”:“invalid name”}`) // Assert the error response
        })
        t.Run("successful", func(t *testing.T) {
            payload.Name = "Varun Sharma"
            resp, code, _, err := makeCall(
                mockServer.URL, "/organization/user",
                http.MethodPost, payload, authHeader, clientTimeout,
            )

            assert.Equal(t, nil, err) // Ensures No error in api call
            assert.Equal(t, http.StatusOK, code) // 200 Response Code
            assert.Equal(t, resp.Name, “Varun Sharma”) // Assert the expected response
            assert.Equal(t, resp.Address, “C5 Kothrud, Pune, Maharashtra”)
            . . .
            })
    })
}    
// Similarly write the tests with expected output for other end-points 
t.Run("Update User", func(t *testing.T) {...
t.Run("Get User", func(t *testing.T) {...
t.Run("Delete User", func(t *testing.T) {...

Some of the critical benefits of TDD

Helps to write Modular and Easy to Maintain Code

By writing tests first, you can ensure that your code is modular and that each piece of code does exactly what it is intended to do. This makes it easier to refactor your code as your project evolves without worrying about breaking existing functionality. In addition, TDD can help you to identify potential problems early in the development process, making it easier to fix them before they become significant issues.

Prevents Regression

TDD is also useful for preventing regression, as it ensures that any changes made to the code do not break existing functionality. In Go, you can run your tests automatically whenever you change your code, making it easy to catch bugs early in development. This can save you significant time and effort; you do not have to test each code change manually.

Think through the code & scenarios.

Another benefit of TDD is that it can help you write more comprehensive code tests. When you write tests first, you can think about the different scenarios and edge cases your code needs to handle. This helps you write complete tests that provide a more accurate picture of how your code will behave in different situations.

Conclusion

In conclusion, Whether you are an experienced developer or just starting, TDD is a powerful tool that can help you to write better code and to improve your software development process. 🟢

Further Reading

Thank you for following along through this journey. I hope you found it helpful and informative. Feel free to reach out @GyaneshSharma_

About the Author

Gyanesh is a Software Engineer at Last9. He enjoys playing around with code, learning new technologies, solving new problems, and improving his skills.