Testability is crucial

#Programming   #GoLang  

Before I decide to add a function I always ask myself, "how can I test this?" Although a plain question, it forces me to re-evaluate the change I wish to make. It also helps me express my code in a more thought out manner.

Testability is having resilient code. This resilience is proved as a programmer attempts to use one class with multiple use cases. In the past, I attempted to write tests for a project I already wrote code for. During these attempts I noticed these classes relied heavily on other aspects of the project. This can be other class references or variables. This made the resulting tests longer and much more complex than it should be. To make this code testable, these references needed to become variables.

Let's get technical

I’ll use some code from Github to demonstrate how I wrote code that was not testable. Within my first week of learning Go I decided to write a web framework. I tried to use only two files to write an entire code base for a framework. This was, undoubtedly, a terrible idea. You can find the contraption at https://github.com/cheikhshift/gos. Here is a piece of sample code to present the problem visually :

package imaginary_web_framework
func handleEveryThing(){
key := GetEnv($var)
...
WriteFile($path)
}

For this project I wrote, there was a function that tried to handle everything with one call and did not return any variables. Theoretically, it is testable to an extent but the resulting test would be complex and require a pamphlet of documentation to understand. This is similar to people saying Go does not have generics, it can, but it would require some hacking. The aim of this piece is to steer you away from hacking and towards writing methodologically. A few things will need to be changed to make the code above testable. The first being that the environment variables can be function parameters. Secondly, the function needs to be broken down, with one handling the logic and the other responsible for writing the file data. The third, have the function return the data that should be written. The code would look something like :

package imaginary_web_framework
func handleSomething(key string) data {
...
return data
}
func writeFile(data interface) bool {
....
WriteFile($path)
}

This new code is easier to test. The new functions have a parameter and returns a data type. The data type returned from each new function can be compared with the desired output. The parameter can be used to try different inputs, but importantly perform fuzzing.

Testing in the Wild

In my earlier days I’d test my code by running the program. Running this once or twice is not a big deal but picture doing it after each change. Tests remove that requirement. Your test logic does the testing. I’ve read Go documentation stating that you should write a test before implementing your function code. This is done to ensure that the function you thought of is written in the form of test code. From that point you can tinker with the function until it passes the test. Besides that, numerous CI/CD platforms support testing and will run before building or deploying code.

Testability is an important aspect of your code. Tests are a great indicator of a codebase's quality. I also believe that any code is testable, even the example given above. However, this would require being extremely ingenious and clever. Clever seemed good when I was younger, now, it frightens me.

Start blogging about your favorite technologies and get more readers

Join other developers and claim your FAUN account now!

Avatar

cheikh

@cheikhseck
Why not? https://t.co/XAvOLLJNhZ
2

Authority

144

Total Hits

Discussed tools
GoGitLab CI/CD