Go SDK tutorial, times10 function
You are a 10x programmer and will soon have your annual performance review, so you are writing a function that takes any number and multiplies it by ten. That way if anybody names some productivity, you can respond with your ten times greater productivity.
You write the function in Go and at first you use print debugging. However, this performance review is very important for your career so you decide not to take any chances. You ultimately decide to thoroughly test this function using the Antithesis Go SDK.
The rest of the document walks you through this process.
Recall that the basic SDK workflow is:
- Include Go SDK in dependencies.
- Use SDK functions in your code.
- Run the Go instrumentor. The instrumentor can be used for:
- Assertion cataloging only,
- Both assertion cataloging and coverage instrumentation.
- Build your Go project.
- Deploy your build.
Creating your project
Step 0: Write your project. You begin by writing your function in Go with print debugging.
Make a directory where you would like to create your Go project and cd
to that directory. (Our directory is /home/me/Projects
– this will be printed in the logs later.)
mkdir myapp
cd myapp
Create a file myapp.go
with the following content:
package main
import (
"fmt"
)
func times10(x int) int {
result := x * 10
return result
}
func main() {
fmt.Println("Hello, world!")
fmt.Printf("%v x 10 = %v\n", 3, times10(3));
fmt.Printf("%v x 10 = %v\n", 8, times10(8));
}
You need to create a go.mod
file. Do that by:
go mod init myorg.myapp
go mod tidy
Note that it’ll have the content similar to this:
module myorg.myapp
go 1.20
Now build and run:
go build
./myorg.myapp
and it prints
Hello, world!
3 x 10 = 30
8 x 10 = 80
Using our SDK functions
Step 1: Include Go SDK in dependencies
Run
go get github.com/antithesishq/antithesis-sdk-go@latest
Note that this updates your go.mod file to something like this
module myorg.myapp
go 1.20
require github.com/antithesishq/antithesis-sdk-go v0.3.3 // indirect
Step 2: Use SDK functions in your code
You wrote a function to multiply numbers by ten. What sorts of properties should it have? The output should always be even. You should also make sure you are testing a wide variety of inputs, such as both even and odd numbers.
Modify the code. The modifications are individually explained below.
package main
import (
"fmt"
"github.com/antithesishq/antithesis-sdk-go/assert"
"github.com/antithesishq/antithesis-sdk-go/random"
)
type Details map[string]any
func times10(x int) int {
assert.Sometimes(x % 2 == 1, "input is sometimes odd", Details{"input": x})
result := x * 10
assert.Always(result % 2 == 0, "result is always even", Details{"result": result})
return result
}
func main() {
fmt.Println("Hello, world!")
for i := 0; i < 50; i++ {
x := int(random.GetRandom() % 500);
fmt.Printf("%v x 10 = %v\n", x, times10(x));
}
}
-
You added two assertions to times10.
-
Line 14 You assert that the result is even using an Always Assertion. This is a fairly conventional assertion.
-
Line 12 You insert a Sometimes Assertion, or an assertion that something will happen at least once across the entire testing session. You assert that sometimes during testing, the function will be called with an odd argument.
The two types of assertions complement one another: Always Assertions assert that the behavior is correct, whereas Sometimes Assertions assert that you are testing under wide enough conditions to surface any potential incorrect behavior. In this case, the output would trivially be even if you only passed it even inputs – you must ensure your properties are not being trivially satisfied!
-
-
Lines 20-22 You use randomness to call the function with many random values (between 0 and 500). Previously, you called the function with hardcoded values but now you will pass the function random values and test that the output is always even. This approach is more powerful but makes Sometimes Assertions necessary – now you must test that you are passing the function odd values, whereas previously the tests were hardcoded so you were certain that you were passing it odd values.
You call the random package’s GetRandom function to draw a random number, and then pass this random number to
times10
. You use a loop to do this fifty times in a row. Every time the times10 function is called in this loop, it triggers the assertions in lines 12 & 14. In summary: You will testtimes10
by passing it a random integer fifty times in a row. You have asserted that all fifty outputs must be even and that at least one random input must be odd.
You will now instrument your project using the Go instrumentor. The instrumentor has two user modes. You decide to use the default, which provides both coverage instrumentation and assertion cataloging.
Instrumenting and compiling
Step 3: Run the Go instrumentor with assertion cataloging and coverage instrumentation
When generating coverage instrumentation, the Go instrumentor will write the instrumented code to a target directory. (Nothing in your local directory will be modified.) The target directory must be an existing but empty directory. Create such a directory at the same level as myapp
:
cd myapp
mkdir ../myapp_generated_instrumentation
Run the Go instrumentor. For this example, we’re generating both assertion cataloging and coverage instrumentation (which is choice 3b in the outline of steps above):
`go env GOPATH`/bin/antithesis-go-instrumentor . ../myapp_generated_instrumentation/
Step 4: Build
Build your project.
cd ../myapp_generated_instrumentation/customer
go build
Ensure that you run the Go instrumentor before you compile your Go code. You must rerun the Go instrumentor every time you add new assertions, so that the new assertions will be cataloged.
If you wanted to disable assertions, you could instead use the build tag no_antithesis_sdk
.
E.g. go build -tags no_antithesis_sdk
Step 5: Deploy your project
You are now ready to build your project. If you are building for local deployment then you would be done here, but suppose instead you intend to send your project to Antithesis for testing. Antithesis will explore your software and search for violations of the properties you have defined.
You must send us containers as described in getting started.
- The executable should be included in your container. In this example,
myapp_generated_instrumentation/customer/myorg.myapp
. - The instrumentation symbols () should be included in your configuration image. You will need to send Antithesis file with precisely the same name as the symbols file. In this example,
myapp_generated_instrumentation/symbols/go-182b876ae1ad.sym.tsv
.
This example is simplified compared to what the full Getting Started guide describes. Note that the function main
is the workload and times10
is the software itself. There are no dependencies. The instrumentation symbols will go in the configuration image as stated.
Ultimately, you will receive a triage report that confirms the properties you defined are true: the output of the function is always even and the inputs to the function are sometimes odd. You now have a well-tested function that can multiply numbers by ten.