Book Image

Test-Driven Development in Go

By : Adelina Simion
Book Image

Test-Driven Development in Go

By: Adelina Simion

Overview of this book

Experienced developers understand the importance of designing a comprehensive testing strategy to ensure efficient shipping and maintaining services in production. This book shows you how to utilize test-driven development (TDD), a widely adopted industry practice, for testing your Go apps at different levels. You’ll also explore challenges faced in testing concurrent code, and learn how to leverage generics and write fuzz tests. The book begins by teaching you how to use TDD to tackle various problems, from simple mathematical functions to web apps. You’ll then learn how to structure and run your unit tests using Go’s standard testing library, and explore two popular testing frameworks, Testify and Ginkgo. You’ll also implement test suites using table-driven testing, a popular Go technique. As you advance, you’ll write and run behavior-driven development (BDD) tests using Ginkgo and Godog. Finally, you’ll explore the tricky aspects of implementing and testing TDD in production, such as refactoring your code and testing microservices architecture with contract testing implemented with Pact. All these techniques will be demonstrated using an example REST API, as well as smaller bespoke code examples. By the end of this book, you’ll have learned how to design and implement a comprehensive testing strategy for your Go applications and microservices architecture.
Table of Contents (18 chapters)
1
Part 1: The Big Picture
6
Part 2: Integration and End-to-End Testing with TDD
11
Part 3: Advanced Testing Techniques

Understanding test metrics

Now that we understand how to deliver projects with tests first, it’s time to look at some metrics that can quantify how well-tested a project is. It’s important to deliver tests across the entire test pyramid, as it’s important to be able to ensure the application is working correctly end-to-end as well as working well with its external dependencies.

Important test metrics

There is a wide range of metrics that we can measure when quantifying the quality of software:

  • Requirement coverage: This indicates the percentage of your project requirements that are covered by tests. A test could cover multiple requirements, but no customer requirement should be left untested.
  • Defect count and distribution: This indicates how many defects or bugs are discovered in each module/part of the application. The distribution will also signal whether there are any particular problem areas in the system that could be refactored.
  • Defect resolution time: This indicates how quickly the development team is able to fix bugs once they are detected. A long Mean Time To Resolution (MTTR) can indicate that the development team is short-staffed, while a long max resolution time in a particular area of the system can indicate that the code in that particular part is difficult to change.
  • Code coverage: This indicates the percentage of your code base that is exercised by unit tests. Since tests should be written first, coverage also shows whether the development team is using TDD. Low test coverage can also indicate issues with the system design.
  • Burndown rates and charts: These indicate the rate at which the team is able to deliver functionality. As development and testing are a unified task, a user story or requirement cannot be considered complete unless it is tested, so the burndown rate will include only stories that are ready for delivery. Burndown charts can indicate delays in project timelines.

Code coverage

Since the code coverage metric is such an important TDD indicator, let’s explore it further. In order to achieve a high coverage percentage, tests should cover the following:

  • The functions you implemented
  • The statements that your functions are composed of
  • The different execution paths of your functions
  • The different conditions of your Boolean variables
  • The different parameter values that can be passed to your functions

The Go test runner provides the coverage percentage for Go applications. We will have a look at how to do this in Chapter 2, Unit Testing Essentials.

Figure 1.11 shows a flow chart of the implementation of the Divide(x,y) function from the simple terminal calculator:

Figure 1.11 – Execution flow of the Divide function in the simple calculator

Figure 1.11 – Execution flow of the Divide function in the simple calculator

Tests should be written to cover and verify the following:

  • The execution path for y != 0
  • The execution path for y == 0
  • The error message of the DivideZero error
  • The output from the result calculation statements
  • The output from the print result statements

Code coverage percentage

In large projects, it will be unfeasible to reach 100% test coverage for the code base. There have been many discussions in the tech community about what a good test coverage percentage is. It is generally accepted that a good coverage amount is around the 80% mark. After that point, experience shows there can be diminishing returns.

The code coverage percentage will also depend on the kind of project you are running. A legacy code base with a low code coverage percentage will require considerable effort to bring up to the 80% mark. Similarly, a greenfield project will also be difficult to test if there are many unknowns.

Just like any code you write, test code needs to be maintained and refactored. Keeping a high coverage percentage requires maintaining and updating a lot of test code. This can increase the development cost of refactoring or other code changes, as it potentially requires updating many test cases. The business value of maintaining tests that do not cover requirements is very low, so you should ensure that your tests are providing value to your test suites.

Well-tested code is not necessarily bug-free code

Your tests should aim to provide verification for important code behavior, as opposed to simply writing code to get a certain code coverage percentage. The team should embrace a testing culture using TDD and a good coverage percentage will follow.