About testing in Go
Tags: opensource
Published: 2021-11-19 14:17:23

This post will be about my [pull request](https://github.com/gempir/gempbot/pull/10) that did some tests with Go.

Here's some backstory about why I chose this [repo](https://github.com/gempir/gempbot). I've been contributing to [7tv](https://github.com/SevenTV), but I'm also a member of their discord community. There I noticed a project that uses 7tv that was also written in Go. A small project from someone in the same community - likely to explain stuff and help out, shouldn't be too difficult of an app. 
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911250826897543178/Screenshot_138.png" />

So I reached out. You can look at my previous post how that went. Long story short, I decided to try my newly acquired knowledge of testing in a different language. The one I'm really interested in - GO.

## How to test? ##

I researched some basic stuff, [official documentation](https://golang.org/doc/tutorial/add-a-test) got me started.

Some specific things I noticed:
 - To make a test file, you have to name it NAME_test.go, instead of NAME.test.js compared to JS.
 - Go has a built in `testing` package, which makes it simple and universal for any GO unit testing.
 - The most useful resource I found is (Go by example)[https://gobyexample.com/testing]. Really shows all you need to begin and make it work. Was even easier than JS, honestly. The table example was really useful too.

## What I did ##
I followed author's advice and made tests for `humanize`. Those functions have simple string/int input and output. I was happy with how fast I figured things out. 

I really a system of testing tables, it looks clean and structured.
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911253461813821461/Screenshot_139.png" />
You can see that I created variables in my test for arguments and the return value I expect. Then I made a table with those variables united in a single object. That way you can later loop through them and check if that combination works together.

Here's some more code. 
 - Function to test:
```go
func StringToSeconds(s string) (int, error) {
	if strings.HasSuffix(s, "m") {
		num := strings.TrimSuffix(s, "m")
		integer, err := strconv.Atoi(num)

		return integer * 60, err
	}

	if strings.HasSuffix(s, "s") {
		num := strings.TrimSuffix(s, "s")
		integer, err := strconv.Atoi(num)

		return integer, err
	}

	integer, err := strconv.Atoi(s)

	return integer, err
}
```

 - The testing function itself:
```go
func TestStringToSeconds(t *testing.T) {
	var tests = []struct {
		str string
		want int
	}{
		{"60s", 60},
		{"1m", 60},
		{"60m", 3600},//Big example
		{"22s", 22},//Basic example
		{"", 0},//Empty string
		{"10d", 0},//Invalid input
	}

	for _, tt := range tests{
		testname:=fmt.Sprintf("%s", tt.str)
		t.Run(testname, func(t *testing.T) {
			ans, err:=StringToSeconds(tt.str)
			if ans != tt.want {
				fmt.Printf("%s",err)
				t.Errorf("Got %d, want %d", ans, tt.want)
			}
		})
	}
}
```

There was finally something I could fix through my testing. I figured out that when this following function returns a value with combined units (minutes and seconds), it returns it in a float format, adding a bunch of `.000000`. Not only it looked bad, it also wasn't consistent with other return values which were ints.
```go
func SecondsToString(s int) string {
	if s < 60 {
		return fmt.Sprintf("%ds", s)
	}
	if s%60 == 0 {
		return fmt.Sprintf("%dm", s/60)
	}

	floored := math.Floor(float64(s) / 60)
	rest := float64(s) - (floored * 60)

	return fmt.Sprintf("%fm %fs", floored, rest)
}
```

This is how it would look with combined values:
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911247618154311721/Screenshot_137.png" />

So I actually changed something in the original code to match the format!
```go
...
	floored := int(math.Floor(float64(s) / 60))
	rest := int(float64(s)) - (floored * 60)

	return fmt.Sprintf("%dm %ds", floored, rest)
}
```

## Next level: Mocks? ##
I was content with myself. I did tests that actually worked. Maybe I can make tests for something more difficult?
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911257381969727548/Screenshot_140.png" />
So my next quest: How do I mock in go? I went to google. There was a problem. I couldn't find a clear and solid answer. So I went on to ask people who might know. 
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911257871235317810/Screenshot_141.png" />
He showed me his repo where he made a bunch of tests, however I couldn't figure it out. So I went in voice to see what he'll explain..

What he showed was.. a lot. Fast. I couldn't follow. He'd jump from document to document, adding in words I didn't know, and I was really too confused to even form a question. Basically...
<img src="https://cdn.discordapp.com/attachments/353432193730871296/911247191929155604/bla_bla1.gif" />
And then he went on to build his new chair that apparently had wrong instructions.

So I decided I'll postpone my learning about mocks in Go. After all, I had enough to make a [PR](https://github.com/gempir/gempbot/pull/10) where things didn't break yet. I still learned new things. Good enough for me. 

*YAY I DID SOMETHING NEW IN GO*