Testing, Stubbing, & Automating Go Training

This coursed was designed to take a student from their first day of testing in Go all the way through creating tests for asynchronous (concurrent) code, testing web API's, and understanding how to properly architect IO patterns for better testing. It will also show students how to configure, isolate, and performance tune their tests. Finally, it will cover several popular Go tools and packages, as well as show how to automate testing and testing workflows.

Length

2 days. Each day is 4 hours long including a 15 minute break.

Class Size

Our classes are priced for small and large classes. We offer classes starting at only five students, up to 100 students. We recognize that each company has specific needs and budgets.

For pricing, fill out our contact us form and you'll receive an automated reply with our current rates.

Target Audience

  • You have been doing daily Go development for 1-3 months.
  • You want to learn more advanced Go testing patterns, such as asynchronous testing, mocking, stubbing, etc.
  • You want to learn how to create faster and more effective tests in Go.

Prerequisites

  • Familiarity and comfort navigating and basic file manipulation at the command line.
  • Familiarity and comfort with a modern code editor, including creating and modifying files and projects.
  • You have 1 to 3 months of daily Go experience.
  • You have at least 6 months of experience with other modern development languages such as Java, C#, Swift, JavaScript, Python, Rust, etc.
  • Familiarity with basic programming concepts and structures such as variables, loops, conditionals, etc.
  • Computers should be capable of modern software development, such as access to install and run binaries, install a code editor, etc. Full instructions referenced here: preparing your environment for Go development. It may be necessary for them to have root/admin access to their computer.

Recommended Preparation

  • Install and configure an editor for Go.
  • Have a functioning Go environment installed with Go 1.13 or later.
  • Sign up for a Github account if you don't already have one.

Suggested Followup Learning

Expected Outcomes

  • Students will understand how to create tests for asynchronous (concurrent) code.
  • Students will be able to stub out Go structures for better unit testing.
  • Students will understand the Go test ecosystem, as well as how to configure and run their Go tests efficiently.
  • Students will learn how to create unit tests and integration tests for web API's.

Course Details

Day One

Welcome To Go Training: Meet Your Instructor And Get Started

In this introductory module, you’ll be introduced to your instructor and the course materials that will guide you through the Go training. You'll find contact details for your instructor, access to the course content, and instructions on setting up your development environment. We'll ensure you’re prepared for a smooth learning experience, with access to all course updates and materials for life. Additionally, you’ll learn how to access further resources and support. Before diving in, we encourage everyone to introduce themselves and share their goals for the course.

Testing Basics: Writing And Running Unit Tests

This chapter introduces the fundamentals of testing in Go, with a focus on writing and running unit tests using the built-in testing package. You'll learn how to structure tests, the importance of naming conventions, and how to utilize the *testing.T type to create effective test cases. Key concepts such as the difference between Error and Fatal, crafting meaningful failure messages, and organizing internal vs. external tests are explored. Additionally, we'll cover useful helper functions, alternative testing packages, and provide practical examples to help you develop reliable, maintainable tests for your Go applications.

Table-Driven Testing For Flexible And Scalable Tests

Table-driven testing is a powerful and efficient way to create clean, reusable, and scalable tests in Go. By defining test cases as data in tables, you can minimize duplication and easily extend test coverage by adding new cases. This chapter covers how to implement table-driven tests, demonstrating how to isolate test logic, reduce redundancy, and ensure your tests remain organized. You will also learn advanced techniques like running sub-tests, parallelizing table-driven tests, and handling setup and teardown logic for enhanced performance and clarity

Automating Testing In Go: Using Go Test And Integrating With Continuous Integration Pipelines

Automating testing is crucial for maintaining code quality and reducing bugs during the development process. This chapter will teach you how to efficiently run and automate tests in Go using the go test command, alongside various useful options such as running specific tests, enabling verbose outputs, handling race conditions, and running tests in parallel. Additionally, you’ll learn how to incorporate tests into Continuous Integration (CI) pipelines for robust and automated feedback on your codebase. By the end, you’ll understand how to structure your tests for fast and effective validation.

Break

Tea/Coffee Break.

Boosting Code Coverage: Tools And Techniques For Comprehensive Testing In Go

This chapter explores advanced tools and techniques for increasing your test coverage and improving overall code quality. You will learn how to generate code coverage reports, interpret coverage statistics, and utilize heatmaps to identify untested code. Additionally, we'll cover isolating specific tests, testing in parallel, and how to leverage Go’s built-in coverage tools to maximize testing efficiency. By the end of this chapter, you'll be equipped to improve your Go project's coverage and maintain high testing standards.

Example Tests

No project is complete without great documentation. Example tests are a great way to not only document your code, but ensure that the examples you use always work as they are actually a test in addition to the documentation.

Isolating Dependencies In Go Tests: Techniques For Creating Reliable Stubs And Fakes

In Go, decoupling components and isolating dependencies during testing can be easily achieved using interfaces. This chapter explores how to create stubs and fakes to test functionality independently and ensure reliability without the need for external dependencies. By simulating different behaviors in your components, you can improve test accuracy and efficiency. You'll learn how to replace real implementations with stubs in unit tests, handle various scenarios like errors and success cases, and maintain high test coverage. With these techniques, you can create more maintainable and flexible test suites for your Go applications.

Day Two

Testing HTTP Handlers With Net/HTTP: Writing Tests For HTTP Services And Utilizing The Net/http/httptest Package

In this chapter, we dive into testing HTTP handlers in Go, leveraging the net/http/httptest package. Testing HTTP handlers can be approached in two distinct styles: Unit Style Testing and Integration Style Testing. Each style serves different purposes in ensuring that your web applications work as expected. We will explore both approaches, demonstrating how to mock HTTP requests, simulate responses, and write efficient tests for your web services. Additionally, you'll learn how to create test servers, utilize middleware, and handle request bodies and form data. By the end of this module, you’ll have the skills to write comprehensive and reliable tests for Go-based web applications.

Testing Asynchronous And Concurrent Code: Techniques For Reliable And Efficient Testing

Testing asynchronous and concurrent code introduces unique challenges, such as dealing with unknown execution times and ensuring test reliability in multi-threaded environments. This chapter focuses on how to efficiently test asynchronous operations, such as database writes, service queues, and distributed system calls, using Go's concurrency primitives like goroutines and channels.

We will cover key techniques for handling these complexities in your tests, including timeouts, retries, and using channels to synchronize test execution. By the end of this chapter, you'll know how to design tests that effectively handle non-blocking, asynchronous processes while maintaining test stability and minimizing bloated test durations. This chapter assumes familiarity with basic concurrency concepts in Go, including the httptest package, channels, and goroutines.

Break

Tea/Coffee Break.

Handling IO In Go Tests: Effective Techniques For Testing IO Operations And External System Interactions

Handling input/output (IO) operations is a crucial part of many software applications, especially when interacting with files, network connections, or other external systems. Testing such interactions presents unique challenges, such as dealing with external dependencies, ensuring proper data flow, and maintaining test isolation.

In this chapter, we will explore how to design Go programs to be easily testable without creating actual files or relying on real external systems. We’ll focus on using Go's io.Reader and io.Writer interfaces to abstract away IO operations, making tests faster, more reliable, and more isolated. Additionally, we’ll explore common patterns, utility types, and functions from Go's standard library to streamline testing for IO-heavy code.

By the end of this chapter, you will have a solid understanding of how to:

  • Abstract IO operations using io.Reader and io.Writer interfaces.
  • Test functions that involve reading and writing data to external systems like files or network connections.
  • Use utility types like strings.Reader, bytes.Buffer, and io.MultiReader to simulate different IO scenarios.
  • Apply advanced testing techniques with the testing/iotest package to ensure robustness in handling IO errors and edge cases.

Enhancing Go Testing With Advanced Tooling

This chapter introduces advanced tools and techniques to improve testing in Go. It covers topics such as:

  • Comparing Complex Structures: A deeper look into comparing values in Go, going beyond reflect.DeepEqual, which often leads to unexpected results, and exploring better alternatives like go-cmp.

  • Using the testing/quick Package: This section explains how to automate tests for a wide variety of input scenarios by leveraging the testing/quick package, which allows for rapid generation of test cases with minimal setup.

  • Fuzz Testing: The chapter also introduces fuzzing as a way to discover edge cases and vulnerabilities in your API. It highlights tools and methods for integrating fuzz testing into your testing strategy to improve code reliability and security.

This chapter provides detailed insights into best practices for using Go’s tooling ecosystem for more effective and comprehensive testing.

Workflow Automation

Workflow automation can significantly improve developer productivity. This chapter will show how to implement some very simple, lightweight automation to automatically run tests and coverage.

Prerequisites

Interfaces And Polymorphism In Go: Understanding Go’s Approach To Interfaces And Dynamic Behavior In Type Systems

Interfaces in Go provide a way to specify the behavior of an object: If something can do this, then it can be used here. This chapter will take a look at how to use interfaces to abstract that behavior. Concepts such as the Empty Interface, satisfying multiple interfaces, and asserting for behavior will be covered. Additionally, this chapter will cover the difference between value and pointer receivers and how they affect the ability to satisfy an interface.

Embedding And Composition For Code Reuse: Using Go’s Embedding Feature For Cleaner, More Modular Code

This chapter delves into Go's powerful embedding and composition mechanisms, which allow developers to build cleaner, modular, and reusable code without traditional inheritance. Instead of subclassing, Go enables embedding types within structs and interfaces, promoting fields and methods automatically. You will explore how embedding works, how method promotion simplifies code, and how to handle collisions and method overriding effectively. This chapter also covers how embedding can be used to satisfy interfaces, making your code more versatile and maintainable through practical examples.

Error Handling And Best Practices (Dealing With Errors, Panics, Recover)

Go's approach to error handling is simple but powerful, emphasizing clear control flow over error-prone constructs like try/catch. In this chapter, you will learn how to effectively manage errors in Go using idiomatic patterns. The chapter covers basic error handling, creating and returning errors, and best practices such as wrapping errors for better context. You will also explore advanced topics like custom error types, sentinel errors, and the use of Go's panic and recover for managing unexpected failures. By the end, you'll have a deep understanding of how Go’s error model leads to more reliable and maintainable code, while learning when and how to use recovery mechanisms for graceful error handling.

Introduction To Go Web Development: Building A Simple HTTP Server And Web Services

In this chapter, you will dive into the basics of web development using Go. You’ll learn how to build a simple web server, create HTTP handlers, and set up routing using Go’s powerful net/http package. Starting with a basic "Hello, World!" web application, we will guide you through key concepts such as routing, serving static files, handling headers, and securing your server with TLS. By the end of this chapter, you’ll have a solid foundation for developing web services in Go, complete with the ability to handle multiple routes and build reliable HTTP applications.

Advanced Routing And Muxing In Go: Building Robust Web Application Routes

This chapter dives deep into the implementation of routing and multiplexing for advanced web development in Go. It covers the basics of using Go’s built-in http.ServeMux for simple routing, along with the limitations of this default multiplexer. The chapter then transitions into constructing more complex routing setups, including:

  • Using Multiple Muxers: Learn how to handle and mount multiple muxers, how path stripping works, and the importance of proper path handling.
  • Custom Muxer Development: A step-by-step guide to building a custom muxer from scratch using Go’s http.Handler interface, illustrating how you can gain full control over your routing logic.
  • Handling HTTP Methods (Verbs): Understand how to create routes that differentiate between HTTP methods such as GET, POST, and DELETE, and route requests accordingly.
  • Limitations of http.ServeMux: Explore why the built-in mux might not be sufficient for production-level applications, leading to the use of third-party routers like gorilla/mux and httprouter.

The chapter also includes practical exercises for building your own muxer and integrating custom routes based on HTTP methods. Through hands-on examples and exercises, developers will gain the skills to create flexible and scalable routing solutions for complex web applications.

Building HTTP Handlers For Web Applications: Handling Routes, Processing Requests, And Sending Responses

In this chapter, we explore the fundamentals of building HTTP handlers in Go, which are the backbone of any web application. You will learn about the http.Handler interface, HandlerFunc, and how to implement them effectively to process requests and send responses. We’ll also delve into how middleware can extend your handlers by adding functionalities such as logging, authentication, and more. By the end of this chapter, you’ll be equipped to create robust and reusable HTTP handlers for your web services, complete with middleware for pre- and post-processing requests.