Part 1: The Big Picture
Chapter 1
Getting to Grips with Test-Driven Development
Exploring the world of TDD
Introduction to the Agile methodology
Types of automated tests
The iterative approach of TDD
TDD best practices
Understanding the benefits and use of TDD
Pros and cons of using TDD
Use case – the simple terminal calculator
Alternatives to TDD
Waterfall testing
Acceptance Test-Driven Development
Understanding test metrics
Important test metrics
Code coverage
Summary
Questions
Further reading
Answers
Chapter 2
Unit Testing Essentials
Technical requirements
The unit under test
Modules and packages
The power of Go packages
Test file naming and placement
Additional test packages
Working with the testing package
The testing package
Test signatures
Running tests
Writing tests
Use case – implementing the calculator engine
Test setup and teardown
The TestMain approach
init functions
Deferred functions
Operating with subtests
Implementing subtests
Code coverage
The difference between a test and a benchmark
Summary
Questions
Further reading
Chapter 3
Mocking and Assertion Frameworks
Technical requirements
Interfaces as dependencies
Dependency injection
Implementing dependency injection
Use case – continued implementation of the calculator
Exploring mocks
Mocking frameworks
Generating mocks
Verifying mocks
Working with assertion frameworks
Using testify
Asserting errors
Writing testable code
Summary
Questions
Further reading
Chapter 4
Building Efficient Test Suites
Technical requirements
Testing multiple conditions
Identifying edge cases
External services
Error-handling refresher
Table-driven testing in action
Step 1 – declaring the function signature
Step 2 – declaring a structure for our test case
Step 3 – creating our test-case collection
Step 4 – executing each test
Step 5 – implementing the test assertions
Step 6 – running the failing test
Step 7 – implementing the base cases
Step 8 – expanding the test case collection
Step 9 – expanding functional code
Parallelization
Advantages and disadvantages of table-driven testing
Use case – the BookSwap application
Testing BookService
Summary
Questions
Further reading
Part 2: Integration and End-to-End Testing with TDD
Chapter 5
Performing Integration Testing
Technical requirements
Supplementing unit tests with integration tests
Limitations of unit testing
Implementing integration tests
Running integration tests
Behavior-driven testing
Fundamentals of BDD
Implementing BDD tests with Ginkgo
Understanding database testing
Useful libraries
Spinning up and tearing down environments with Docker
Fundamentals of Docker
Using Docker
Summary
Questions
Further reading
Chapter 6
End-to-End Testing the BookSwap Web Application
Technical requirements
Use case – extending the BookSwap application
User journeys
Using Docker
Persistent storage
Running the BookSwap application
Exploring Godog
Implementing tests with Godog
Creating test files
Implementing test steps
Running the test suite
Using database assertions
Seed data
Test cases and assertions
Summary
Questions
Further reading
Chapter 7
Refactoring in Go
Technical requirements
Understanding changing dependencies
Code refactoring steps and techniques
Technical debt
Changing dependencies
Relying on your tests
Automated refactoring
Validating refactored code
Error verification
Custom error types
Splitting up the monolith
Key refactoring considerations
Summary
Questions
Further reading
Chapter 8
Testing Microservice Architectures
Technical requirements
Functional and non-functional testing
Performance testing in Go
Implementing performance tests
Contract testing
Fundamentals of contract testing
Using Pact
Breaking up the BookSwap monolith
Production best practices
Monitoring and observability
Deployment patterns
The circuit breaker pattern
Summary
Questions
Further reading
Part 3: Advanced Testing Techniques
Chapter 9
Challenges of Testing Concurrent Code
Technical requirements
Concurrency mechanisms in Go
Goroutines
Channels
Applied concurrency examples
Closing once
Thread-safe data structures
Waiting for completion
Issues with concurrency
Data races
Deadlocks
Buffered channels
The Go race detector
Untestable conditions
Use case – testing concurrency in the BookSwap application
Summary
Questions
Further reading
Chapter 10
Testing Edge Cases
Technical requirements
Code robustness
Best practices
Usages of fuzzing
Fuzz testing in Go
Property-based testing
Use case – edge cases of the BookSwap application
Summary
Questions
Further reading
Chapter 11
Working with Generics
Technical requirements
Writing generic code in Go
Generics in Go
Exploring type constraints
Table-driven testing revisited
Step 1 – defining generic test cases
Step 2 – creating test cases
Step 3 – implementing a generic test run function
Step 4 – putting everything together
Step 5 – running the test
Test utilities
Extending the BookSwap application with generics
Testing best practices
Development best practices
Testing best practices
Culture best practices
Summary
Questions
Further reading
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.
Create practical Go unit tests using mocks and assertions with Testify
Build table-driven test suites for HTTP web applications
Write BDD-style tests using the Ginkgo testing framework
Use the Godog testing framework to reliably test web applications
Verify microservices architecture using Pact contract testing
Develop tests that cover edge cases using property testing and fuzzing
If you are an intermediate-level developer or software testing professional who knows Go fundamentals and is looking to deliver projects with Go, then this book is for you. Knowledge of Go syntax, structs, functions, and interfaces will help you get the most out of this book.