Day Five queued and ready to go
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 2m1s
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 2m1s
This commit is contained in:
parent
2209eef082
commit
e97aa7b016
127
src/blog/posts/2024/5/learning-go-day-5.md
Normal file
127
src/blog/posts/2024/5/learning-go-day-5.md
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
---
|
||||||
|
title: "Learning Go: Day Five"
|
||||||
|
date: 2024-05-05T08:00:00.0Z
|
||||||
|
tags:
|
||||||
|
- learning
|
||||||
|
- go
|
||||||
|
excerpt: "For Day Five, I'm going to look at how to write tests"
|
||||||
|
---
|
||||||
|
|
||||||
|
Testing is important! I'm an advocate for Test-Driven Development in my work[^1], so it's quite important that I work out how to test what I'm writing before I go any further. For today, I'm using [this really helpful blog post](https://blog.jetbrains.com/go/2022/11/22/comprehensive-guide-to-testing-in-go/) from Jetbrains as a guide.
|
||||||
|
|
||||||
|
## Creating a test
|
||||||
|
|
||||||
|
First of all, I need a separate file for my tests. The convention is apparently to store tests alongside the code they're testing, so I'll create `maths_test.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// maths_test.go
|
||||||
|
package maths
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestMultiply(t *testing.T) {
|
||||||
|
result := Multiply(2, 5)
|
||||||
|
if result != 10 {
|
||||||
|
t.Errorf("Got %d when expecting %d", result, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This wrapping function seems to be used similarly to a `describe` block if you're writing tests using `jest` in Javascript. That is, it's a descriptor for a collection of tests, not necessarily a single test.
|
||||||
|
|
||||||
|
Tests are ran using the `go test` command, however when I ran mine I got this output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test
|
||||||
|
? lewisdale.dev/learn-go [no test files]
|
||||||
|
```
|
||||||
|
|
||||||
|
It turns out I had to specify the package because I'm running it from the module root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test lewisdale.dev/learn-go/maths
|
||||||
|
ok lewisdale.dev/learn-go/maths 0.138s
|
||||||
|
```
|
||||||
|
|
||||||
|
And then searching tells me I can also use `./...` as the package name to run all tests recursively:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
ok lewisdale.dev/learn-go/maths 0.138s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Triangulating tests
|
||||||
|
|
||||||
|
These are call table-driven tests, and they amount to iterating over an array (or slice!) of inputs and running a test for each input:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestMultiply(t *testing.T) {
|
||||||
|
var inputs = []struct {
|
||||||
|
a, b, expected int
|
||||||
|
}{
|
||||||
|
{2, 5, 10},
|
||||||
|
{10, 100, 1000},
|
||||||
|
{12, 15, 180},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, input := range inputs {
|
||||||
|
t.Run(fmt.Sprintf("%d x %d = %d", input.a, input.b, input.expected), func (t *testing.T) {
|
||||||
|
if result := Multiply(input.a, input.b); result != input.expected {
|
||||||
|
t.Errorf("Got %d when expecting %d", result, input.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The command line doesn't give much information by default, but by adding the verbose (`-v`) flag to the test command we get much better output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./... -v
|
||||||
|
? lewisdale.dev/learn-go [no test files]
|
||||||
|
=== RUN TestMultiply
|
||||||
|
=== RUN TestMultiply/2_x_5_=_10
|
||||||
|
=== RUN TestMultiply/10_x_100_=_1000
|
||||||
|
=== RUN TestMultiply/12_x_15_=_180
|
||||||
|
--- PASS: TestMultiply (0.00s)
|
||||||
|
--- PASS: TestMultiply/2_x_5_=_10 (0.00s)
|
||||||
|
--- PASS: TestMultiply/10_x_100_=_1000 (0.00s)
|
||||||
|
--- PASS: TestMultiply/12_x_15_=_180 (0.00s)
|
||||||
|
PASS
|
||||||
|
ok lewisdale.dev/learn-go/maths 0.109s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fuzzing tests
|
||||||
|
|
||||||
|
This is a pretty cool feature. Go has a built-in test fuzzer that produces random values and can be used to find edge cases:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FuzzMultiply(f *testing.F) {
|
||||||
|
f.Add(2, 5)
|
||||||
|
f.Fuzz(func (t *testing.T, a, b int) {
|
||||||
|
Multiply(a, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And then run using `cd maths && go test -fuzz .`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -fuzz .
|
||||||
|
fuzz: elapsed: 0s, gathering baseline coverage: 0/1 completed
|
||||||
|
fuzz: elapsed: 0s, gathering baseline coverage: 1/1 completed, now fuzzing with 10 workers
|
||||||
|
fuzz: elapsed: 3s, execs: 1358234 (452687/sec), new interesting: 0 (total: 1)
|
||||||
|
fuzz: elapsed: 6s, execs: 2742335 (461339/sec), new interesting: 0 (total: 1)
|
||||||
|
fuzz: elapsed: 9s, execs: 4126851 (461439/sec), new interesting: 0 (total: 1)
|
||||||
|
fuzz: elapsed: 12s, execs: 5525464 (466204/sec), new interesting: 0 (total: 1)
|
||||||
|
^Cfuzz: elapsed: 12s, execs: 5737472 (471088/sec), new interesting: 0 (total: 1)
|
||||||
|
PASS
|
||||||
|
ok lewisdale.dev/learn-go/maths 12.594s
|
||||||
|
```
|
||||||
|
|
||||||
|
This ran my function almost 6 million times in 12 seconds, which is wild. This is a _really_ useful tool for picking up those hard-to-find bugs, but because it will run until there's an error it almost certainly won't be something to use in CI.
|
||||||
|
|
||||||
|
That's all I'm going to cover on testing today, there's quite a lot more there that I'll start to pick through as I start working on an actual project[^2]. One of the more useful things in the Jetbrains post includes the [Testify package](https://github.com/stretchr/testify), which looks like a testing framework that wraps `testing` for less-verbose tests.
|
||||||
|
|
||||||
|
[^1]: At least, when I remember to do it, otherwise it's DDT.
|
||||||
|
[^2]: Which I will decide on in the next couple of days
|
Loading…
Reference in New Issue
Block a user