Testing with Go
Course description
Learn how to test real, complex software written in Go. Large or small, perfect abstractions or global state galore; it doesn't matter what your code looks like, you CAN learn to test it. Tell me if this sounds familiar - you are learning how to test in Go, and things seem to be going great. The tutorials are all clicking, and you can't wait to start applying what you are learning in your real projects.
Read more about the course
You fire up your editor, grab your latest project, create your first *_test.go source file, and suddenly it feels like you don't have a clue what you are doing.
*You hear the sound of glass shattering*
What happened?!?!
Things were going so great. All those examples made sense, but now you don't even know where to start.
It seemed so easy to test that "Hello, world" HTTP handler, but how do you test complex handlers? You know, HTTP handlers that do something realistic like insert a record into a database, or use an API to verify someone's address.
For that matter, how do we verify that our database interactions are working as we expected? Or maybe your app has a global DB variable - does that mean testing simply isn't possible?
What about those APIs we are interacting with? Do we stub them? Do we hit the test API? What happens if we hit API rate limits or there isn't even a test API?
Alright, alright! Take a second to breathe and let me fill you in on a little secret...
Testing isn't hard, but simple tutorials don't do it justice
Testing isn't any harder than writing any other Go code. In fact, if we wanted we could test our code by just writing a main package and interacting with our application code. We could panic when something doesn't act the way we expected, and viola - we have a test!
But why does it feel so hard? Probably because we simplify the examples to the point that they lose all of their value.
Think about it, when is the last time you wrote a Palindrome function?
Never? So why are all these tutorials showing us how to test one?
Why aren't they showing us how to test realistic software? What happened to the example where we test a real HTTP handler that needs access to a data store? Or the tutorial where we build an API client and learn how to test it WITHOUT always hitting the real API. Wouldn't it be great if we could learn how to test a real web app with a real DB and a real integration to a payments API like Stripe?
Test with Go is different.
In this course you will learn how to test REAL software, not palindrome functions.
We will have to look at a few isolated examples in order to learn specific testing techniques, but that isn't enough to solidify a concept so we won't stop there. We will build real projects that teach you how to apply all of these testing skills in real software.
In one project we build a web application which allows us to address the complexities that come up while testing an application that uses a database, third party APIs, and more. In another project we look at how internal testing helps us verify our intermediate steps are correct, while also discussing the downside to testing unexported functions.
You will learn about common pitfalls to avoid in order to write more testable code. You will learn how to incrementally fix code that has already succumbed to many of these pitfalls, allowing you to avoid a massive PR that makes your reviewer cry inside.
When you run into an application with a global DB variable you won't need to give up on testing. You will learn exactly how to make small, manageable changes to the code that allow you to start testing it almost immediately.
The next time you are asked whether the data store should be mocked or if a real SQL database should be used you will be able to discuss the pros and cons of both approaches with your teammates and decide on a proper plan of action.
After completing this course you will have the knowledge and the skills necessary to start testing your own projects. You will still have to put in the work, but the mystery, the confusion, and the frustration will be gone.
Watch Online
| # | Lesson Title | Duration |
|---|---|---|
| 1 | 1 - What is a test? | 03:13 |
| 2 | 2 - Why do tests matter? | 09:37 |
| 3 | 3 - Writing great tests | 25:30 |
| 4 | 4 - Testing with a main package | 06:57 |
| 5 | 5 - Testing with Go's testing package | 08:22 |
| 6 | 6 - What happens when we run go test | 07:03 |
| 7 | 7 - File naming conventions | 05:46 |
| 8 | 8 - Function naming conventions | 04:48 |
| 9 | 9 - Variable naming conventions | 05:53 |
| 10 | 10 - Ways to signal test failure | 08:19 |
| 11 | 11 - When to use Error vs Fatal | 17:38 |
| 12 | 12 - Writing useful failure messages | 17:41 |
| 13 | 13 - A basic example as a test case | 06:50 |
| 14 | 14 - Viewing examples in the docs | 05:49 |
| 15 | 15 - Unordered example output | 04:28 |
| 16 | 16 - Complex examples | 15:29 |
| 17 | 17 - Examples in the standard library | 04:20 |
| 18 | 18 - Table driven tests | 10:47 |
| 19 | 19 - Generating table driven test code | 05:01 |
| 20 | 20 - Subtests | 10:47 |
| 21 | 21 - Shared setup and teardown | 13:40 |
| 22 | 22 - TestMain | 10:09 |
| 23 | 23 - Running tests in parallel | 10:10 |
| 24 | 24 - Parallel subtests | 04:02 |
| 25 | 25 - Setup and teardown with parallel subtests | 05:02 |
| 26 | 26 - Gotchas with closures and parallel tests | 12:54 |
| 27 | 27 - What is a race condition? | 08:33 |
| 28 | 28 - The race detection flag | 06:10 |
| 29 | 29 - Testing explicitly for race conditions | 20:38 |
| 30 | 30 - Simple comparisons | 07:45 |
| 31 | 31 - Reflect's DeepEqual function | 03:11 |
| 32 | 32 - Golden files (brief overview) | 02:59 |
| 33 | 33 - Helper comparison functions | 22:38 |
| 34 | 34 - Building things with helper functions | 18:53 |
| 35 | 35 - Generating test data | 04:50 |
| 36 | 36 - Go's testing/quick package | 05:56 |
| 37 | 37 - Public testing utilities | 09:36 |
| 38 | 38 - Running specific tests | 05:29 |
| 39 | 39 - Running tests for subpackages | 04:41 |
| 40 | 40 - Skipping tests | 03:06 |
| 41 | 41 - Custom flags | 04:02 |
| 42 | 42 - Build tags | 05:26 |
| 43 | 43 - Benchmarks | 22:40 |
| 44 | 44 - Verbose testing | 02:22 |
| 45 | 45 - Code coverage | 17:01 |
| 46 | 46 - The timeout flag | 04:19 |
| 47 | 47 - Parallel testing flags | 11:00 |
| 48 | 48 - Differences between external and internal tests | 06:36 |
| 49 | 49 - How to write internal and external tests | 06:15 |
| 50 | 50 - When to use external tests | 09:40 |
| 51 | 51 - Exporting unexported vars, funcs, and types | 09:44 |
| 52 | 52 - When to use internal tests | 08:14 |
| 53 | 53 - Overview of test types | 05:03 |
| 54 | 54 - Unit tests | 01:50 |
| 55 | 55 - Integration tests | 09:15 |
| 56 | 56 - End-to-end tests | 06:35 |
| 57 | 57 - Which test type should I use? | 09:51 |
| 58 | 58 - What is global state? | 09:23 |
| 59 | 59 - Testing with global state (if you must) | 06:48 |
| 60 | 60 - What is dependency injection (DI)? | 04:44 |
| 61 | 61 - DI enables implementation agnostic code | 09:21 |
| 62 | 62 - DI makes testing easier | 12:08 |
| 63 | 63 - DI and useful zero values | 10:58 |
| 64 | 64 - Removing global state with DI | 08:16 |
| 65 | 65 - Package level functions | 14:19 |
| 66 | 66 - Summary of dependency injection | 11:15 |
| 67 | 67 - What is mocking? | 05:52 |
| 68 | 68 - Types of mock objects | 10:34 |
| 69 | 69 - Why do we mock? | 12:44 |
| 70 | 70 - Third party packages | 05:08 |
| 71 | 71 - Faking APIs | 15:57 |
| 72 | 72 - What are interface test suites? | 14:14 |
| 73 | 73 - Interface test suite setup and teardown | 08:44 |
| 74 | 74 - Interface test suites in the wild | 06:24 |
| 75 | 75 - httptest.ResponseRecorder | 11:29 |
| 76 | 76 - httptest.Server | 04:47 |
| 77 | 77 - Building HTTP helpers | 18:51 |
| 78 | 78 - What are golden files? | 10:19 |
| 79 | 79 - Updating golden files | 04:25 |
| 80 | 80 - What is a subprocess? | 04:06 |
| 81 | 81 - Running the subprocess in tests | 07:44 |
| 82 | 82 - Mocking simple subprocesses | 05:58 |
| 83 | 83 - Mocking complex subprocesses | 24:06 |
| 84 | 84 - Why are dates and times problematic? | 07:08 |
| 85 | 85 - Inject your time and sleep functions | 16:12 |
| 86 | 86 - Testing timeouts | 14:45 |
| 87 | 87 - Colorizing your terminal output | 03:22 |
| 88 | 88 - Coverage info function | 01:31 |
| 89 | 1 - Topics covered in the form project | 06:28 |
| 90 | 2 - The first test | 11:33 |
| 91 | 3 - Our first bug | 05:10 |
| 92 | 4 - Handling multiple fields | 05:54 |
| 93 | 5 - Field values | 07:53 |
| 94 | 6 - Checking for specific attributes in a test | 18:52 |
| 95 | 7 - Unexported fields | 05:11 |
| 96 | 8 - Non-structs are invalid | 08:52 |
| 97 | 9 - Pointers to structs | 03:51 |
| 98 | 10 - Supporting more types | 16:52 |
| 99 | 11 - Generating HTML | 17:36 |
| 100 | 12 - Discussing struct tags and tests | 17:19 |
| 101 | 13 - Parsing struct tags | 25:23 |
| 102 | 14 - Applying struct tags | 26:50 |
| 103 | 15 - Golden test files | 16:02 |
| 104 | 16 - Struct tag tests in TestHTML | 08:00 |
| 105 | 17 - Rendering errors: adding a test | 19:29 |
| 106 | 18 - Rendering errors: implementing | 06:22 |
| 107 | 19 - Detecting breaking changes with tests | 09:56 |
| 108 | 1 - The first test | 12:41 |
| 109 | 2 - Creating a customer | 09:25 |
| 110 | 3 - Versioning our client | 02:26 |
| 111 | 4 - Making the API key a flag | 02:37 |
| 112 | 5 - Improving our customer data | 04:46 |
| 113 | 6 - The charge endpoint | 15:33 |
| 114 | 7 - Custom error type | 23:15 |
| 115 | 8 - Parsing stripe errors | 20:01 |
| 116 | 9 - Customer endpoint errors | 14:58 |
| 117 | 10 - Starting on unit tests | 11:06 |
| 118 | 11 - Allowing custom http clients | 14:40 |
| 119 | 12 - Creating a recorder client | 23:24 |
| 120 | 13 - Persisting recorded responses | 12:02 |
| 121 | 14 - Making our tests cross-platform | 02:24 |
| 122 | 15 - Serving recorded responses | 12:54 |
| 123 | 16 - Unique customer per charge subtest | 09:53 |
| 124 | 17 - Adding tests for specific errors | 10:40 |
| 125 | 18 - Helper functions | 12:56 |
| 126 | 1 - What to expect | 15:02 |
| 127 | 2 - App overview | 12:43 |
| 128 | 3 - Initial db tests | 14:42 |
| 129 | 4 - Creating the db.Open function | 12:18 |
| 130 | 5 - What about mocks | 07:09 |
| 131 | 6 - Test harnesses and helpers | 23:02 |
| 132 | 7 - Reviewing tests | 22:33 |
| 133 | 8 - Testing specific times | 05:21 |
| 134 | 9 - First pass at refactoring the db pkg | 12:04 |
| 135 | 10 - Updating db tests | 32:31 |
| 136 | 11 - Testing the order flow | 01:03:07 |
| 137 | 12 - Extracting code for unit testing | 17:03 |
| 138 | 13 - Extracting the active campaign handler | 16:15 |
| 139 | 14 - Unit testing the active campaign handler | 26:06 |
| 140 | 15 - Table driven testing the active campaign handler | 24:22 |
| 141 | 16 - Refactoring campaign middleware | 12:36 |
| 142 | 17 - Unit testing the campaign middleware | 21:42 |
| 143 | 18 - Starting the orders handler | 06:53 |
| 144 | 19 - Testing the new order handler | 18:23 |
| 145 | 20 - Refactor Create order handler | 09:44 |
| 146 | 21 - Test: Create order handler | 20:00 |
| 147 | 22 - Integration testing with Stripe | 15:20 |
| 148 | 23 - Testing for specific Stripe failures | 07:25 |
| 149 | 24 - Another form of table driven tests | 31:08 |
| 150 | 25 - Refactor: Order middleware | 04:03 |
| 151 | 26 - Test: Order middleware | 06:08 |
| 152 | 27 - Refactor: Show order handler | 06:36 |
| 153 | 28 - Test: Show order handler | 33:32 |
| 154 | 29 - Don't get too clever | 10:21 |
| 155 | 30 - Integration testing the show order handler | 12:41 |
| 156 | 31 - Removing sql from the confirm order handler | 09:40 |
| 157 | 32 - Testing the database confirm order function | 12:53 |
| 158 | 33 - Refactor: Confirm order handler | 04:41 |
| 159 | 34 - Test: Confirm order handler with same address | 14:34 |
| 160 | 35 - Test: Confirm order handler when campaign isnt found | 06:40 |
| 161 | 36 - Refactoring and finishing the confirm order handler unit tests | 29:01 |
| 162 | 37 - Integration test: Confirm order handler | 15:32 |
| 163 | 38 - Setting the stripe secret key via ENV variable | 06:07 |
| 164 | 39 - Refactoring the routing code in main | 13:05 |
| 165 | 40 - Testing our asset directory | 11:33 |
| 166 | 41 - Mocks for testing our router | 09:18 |
| 167 | 42 - Testing the show order route | 10:06 |
| 168 | 43 - Table driven router testing | 08:14 |
| 169 | 44 - Additional router testing | 07:05 |
| 170 | 45 - Removing the tempDB type | 16:18 |
| 171 | 46 - Removing the DefaultDatabase package variable | 22:27 |
| 172 | 47 - Cleanup | 10:27 |
| 173 | 48 - Wrapping up | 08:58 |
Comments
0 commentsWant to join the conversation?
Sign in to commentSimilar courses
Learn How To Code: Google's Go (golang) Programming Language
Ultimate Go: Software Design with Kubernetes 2.0
Build a Google Analytics in Go
Mastering Multithreading Programming with Go (Golang)