- Lab
- Core Tech

Guided: Discovering Edge Case with Go Fuzzing
In this Code Lab, you’ll explore Go Fuzzing to uncover hidden edge cases in critical code functions. You'll be guided through setting up and running fuzz tests, using randomized inputs to detect unexpected behaviors and enhance code resilience across diverse scenarios. By the end, you’ll have practical skills in Go Fuzzing to improve the reliability and robustness of your applications, ensuring they are production-ready and resilient to real-world data.

Path Info
Table of Contents
-
Challenge
Introduction
In this lab, you'll explore Go Fuzzing, a technique that generates random inputs to test your functions. Fuzzing helps uncover edge cases that traditional testing might miss, pushing your code into unexpected states to catch potential bugs early.
Scenario
You are a Go developer working on a critical component of an internal system for your company. The system includes a function designed to check whether a string is a palindrome, a function that must work flawlessly across various input types, including different character encodings, special characters, and very large strings.
Currently, the function works for basic cases, but there's a growing concern that it may fail under edge cases not yet considered in testing. Your objective is to rigorously test this function using Go.
Before you begin, here are some key points:
- Your task involves implementing code in the
palindrome.go
andpalindrome_test.go
files. - If you encounter any challenges along the way, feel free to consult the
solution
directory. - To simplify the process, comments are included to help you find the necessary changes for each task.
- You can execute the fuzz tests by typing
go test -fuzz=FuzzIsPalindrome
command in the Terminal. - Fuzz tests don't exit automatically, you'll have to press
Control+C
(orCommand+C
on macOS) to terminate the tests.
- Your task involves implementing code in the
-
Challenge
Palindrome
What is a Palindrome?
A palindrome is a word, phrase, number, or sequence of characters that reads the same forward and backward, meaning it remains unchanged when its letters or digits are reversed.
Examples:
- madam
- racecar
- level
- 1001
- A Santa at NASA
- Step on no pets
Palindromes are case-insensitive. For example, "Racecar" and "racecar" are considered the same.
Understanding Palindrome Logic
In the
palindrome.go
file, the functionIsPalindrome(s string)
checks if a string is a palindrome.It works by iterating over the first half of the string and then:
- For each character at index
i
, it compares it with the character at the corresponding position from the end(len(s)-1-i)
. - If any pair doesn't match, it returns
false
(not a palindrome). - If all pairs match, it returns
true
(is a palindrome).
Review and run the basic test case
TestIsPalindrome
in thepalindrome_test.go
file. As you observed in this case, you've specified a limited set of pre-defined test cases. Go fuzzing, however, can intelligently generate a variety of inputs to test the function more thoroughly.In the following steps, you will implement Go fuzzing.
-
Challenge
Create Fuzz Function
What is Fuzzing?
Go Fuzzing is a testing technique in Go (introduced in Go 1.18) that automatically generates random inputs to test your code. The goal of fuzzing is to find edge cases, unexpected behaviors, or bugs that might not be covered by standard unit tests. Instead of writing specific test cases, fuzzing dynamically creates and tests many input variations, helping to uncover hard-to-find issues.
How Go Fuzzing Works:
-
Seed Inputs: You start with known inputs (or a "corpus") that the fuzzing engine will use as a starting point.
-
Input Generation: The fuzzer then mutates these inputs to generate a wide variety of inputs, both valid and invalid.
-
Bug Discovery: If the fuzzed input causes a failure (like a crash, panic, or wrong result), Go reports the problematic input for further investigation.
How to Create a Fuzz Function:
-
Import Required Packages:
Import thetesting
package to utilize Go's built-in fuzzing functionality. -
Create a Fuzz Function:
Define a function that begins withFuzz
and takes a pointer totesting.F
as an argument. This function will handle the setup for the fuzzing process. -
Seed the Fuzzer:
Optionally, use thef.Add
method to add initial values that will seed the fuzzer. This helps specify known inputs to test against. -
Define the Fuzzing Logic:
Inside the fuzzer, create an anonymous function that accepts randomly generated inputs. This function should also take a pointer totesting.T
as an argument. -
Test Your Function:
Within the anonymous function, call the function you want to test with the generated input. Implement any necessary assertions or checks to validate the output.
With the basic structure of
FuzzIsPalindrome
defined inpalindrome_test.go
, you will now seed the fuzz function with test values. These inputs should cover a variety of scenarios that the target functionIsPalindrome
needs to handle. Next, you'll define the fuzzing logic to verify that the output generated byIsPalindrome
is as expected. In this step, you created a fuzz function, seeded it with default values, and implemented assertions to verify that the generated values are correct.In the next step, you are going to run the fuzz test to identify the edge cases.
-
-
Challenge
Identify and Fix Edge Test Cases
In this step, you will run the fuzz test you've created to check for potential edge cases in the
IsPalindrome
function.Understanding the Output of the Fuzzing Test
As the fuzzer runs, it generates inputs based on the cases specified in the code and mutates these inputs to explore additional edge cases.
During this process, you may see output like:
fuzz: elapsed: 3s, executions: 10000 (3300/sec), new interesting: 1
This line shows the number of inputs the fuzzer has tested and how many of them led to new or "interesting" coverage paths, indicating the fuzzer’s attempt to explore more complex cases.
Failure Detection
If the fuzzer finds an input that causes a failure (like a panic, crash, or incorrect result), it will log a message showing the input that led to the problem:
--- FAIL: FuzzIsPalindrome (0.00s) palindrome_test.go:35: Mismatch found for input: "Rotor"
Summary of Results
At the end, you’ll see an overall result indicating whether the test passed or failed:
FAIL exit status 1
If the test passes without finding any mismatches or issues, it will show:
ok <package> <duration>
This indicates that the function handled all the fuzzed inputs correctly.
Corpus File Creation
If a failure is found, Go may save the failing input to a corpus file, ensuring that this specific input is tested in future runs to prevent regression.
Failure details will be saved at the following location:
\testdata\fuzz\FuzzIsPalindrome\
.This corpus file builds a persistent set of edge cases, which can be used to further strengthen your tests over time.
The current code in the
isPalindrome
function does not supportunicode
characters. So once the fuzz function is run, you should get a similar output in the corpus file, though with varying value.go test fuzz v1 string("\xcd")
Handling Multi-byte Input Using Runes
The original code compared characters directly, which doesn’t handle
multi-byte Unicode
characters properly.In Go, a
rune
is an alias forint32
and represents a single Unicode code point. It is specifically used to work with individual characters as Unicode, rather than as simple bytes.In order to fix this, you will now convert the
string
to aslice
ofrunes
([]rune)
, enabling accurate Unicode character processing. You can now rerun the fuzz test using the following command:go test -fuzz=FuzzIsPalindrome
info> Don't forget to stop the fuzz test by typing
Control+C
(orCommand+C
on macOS) in the Terminal.In the next step, you are going to look into optimizing the performance of fuzz testing.
-
Challenge
Optimize Fuzz Testing
While fuzz testing is a powerful tool, it can sometimes generate inputs that are too large, causing performance issues. For instance, testing strings that are thousands of characters long can slow down your fuzz tests unnecessarily. To handle this, you’ll implement constraints that limit input size based upon your business requirements.
You will modify the fuzz function to skip strings longer than 1,000 characters.
Another way to optimize fuzzing time is by adjusting the fuzzing parameters. You can manage the duration and complexity of the fuzzing process using specific Go test flags. These parameters help achieve a balance between test coverage and performance.
- Use the
-fuzztime
flag to set a limit on the duration of the fuzz test, for example, 20 seconds. - Use the
-parallel
flag to specify the number of fuzz tests to run in parallel, which is beneficial for multi-core processors.
For instance, if you have a PC with an 8-core processor and want the fuzzing to run for 20 seconds, you would use the following command:
go test -fuzz=FuzzIsPalindrome -fuzztime=20s -parallel=8
In addition to limiting input size, you can fine-tune your fuzz testing by manually adding inputs to the fuzzing corpus (like strings with special characters) to ensure they are tested more often.
In the next step, you'll further optimize the
IsPalindrome
function to handle all cases. - Use the
-
Challenge
Handle Advanced Edge Cases
In this step, you’ll enhance the
IsPalindrome
function to support additional cases, such as ignoring spaces and making it case-insensitive when checking for palindromes.Example : A Santa at NASA
This example demonstrates both criteria: ignoring spaces and matching without considering case.
To streamline the process, the functions
removeSpaces
andtoLowerTextRunes
have already been implemented in theutil.go
file, and you'll be reusing them in your code.First, you’ll update the
palindrome_test.go
file to expect an error in the fuzzing step, then fix that error in theIsPalindrome
function.Now, you’ll adjust the fuzzing logic to account for these new conditions.
Remove spaces from the expected output in the test file, then run the fuzzer. Next, run the fuzz test.
go test -fuzz=FuzzIsPalindrome
After running the fuzz test, observe the failing results saved in the
\testdata
directory.Next, update the
IsPalindrome
function in thepalindrome.go
file to remove spaces from the input. Now, rerun the fuzz test with the following command:go test -fuzz=FuzzIsPalindrome
Let the test run for a few seconds, then stop it. The result should display as PASS.
Next, in order to achieve a case insensitive check of palindrome, you'll update the
palndrome_test.go
andpalindrome.go
files.Now, rerun the fuzz test with the following command:
go test -fuzz=FuzzIsPalindrome
The test will fail for case sensitive match, observe the failure case in
\testdata
directory. Now you'll update theisPalindrome
function and fix this. Great! Now, if you rerun the fuzz test, it should complete without reporting any issues.Don't forget to stop the fuzz test by pressing
Control+C
(orCommand+C
on macOS).In this step, you updated the fuzz function to handle edge cases like spaces and case insensitivity, observing how fuzz testing efficiently catches potential issues and ensures robust testing.
With these updates, you can be confident that the palindrome functionality is well-prepared for future inputs.
-
Challenge
Conclusion & Next Steps
Conclusion
In this lab, you've explored the powerful technique of Go fuzzing and how it can be leveraged to discover edge cases that would be difficult to find with traditional testing methods. By integrating fuzz testing into your Go projects, you’ve learned how to uncover hidden bugs by automatically generating random inputs to stress-test your functions.
You've worked through configuring fuzz tests and identified and addressed edge cases, such as handling special characters, Unicode, and large inputs. Additionally, you’ve optimized your fuzz tests to efficiently test more complex scenarios and improved the overall robustness of your code by refining your palindrome function to handle advanced edge cases.
With Go fuzzing, you've enhanced your testing strategy, making your code more resilient and better equipped to handle unexpected inputs. This process not only strengthens your application's reliability, but also ensures its long-term stability in production environments.
Next Steps
-
Apply Fuzz Testing to Other Functions: Start applying fuzz testing techniques to other critical functions in your Go projects. Test string manipulation, numerical calculations, and file processing functions to ensure they are robust against edge cases.
-
Explore Advanced Fuzzing Techniques: Dive deeper into advanced fuzzing topics such as coverage-guided fuzzing, which focuses on increasing code coverage with each test run, and customizing fuzzers to target specific vulnerabilities or edge cases.
-
Integrate Fuzzing into Your CI Pipeline: Implement fuzz tests in your continuous integration (CI) pipeline to continuously test for potential issues as your code evolves.
-
Keep Experimenting with Fuzzing Tools: Experiment with other fuzzing tools and libraries in Go and explore how they can be integrated into your development and testing processes for even more comprehensive testing.
By practicing these steps, you will continue to improve your fuzz testing skills, making your Go code even more reliable and secure.
Congratulations on completing this lab!
-
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.