Edited the about file, new post on probability and testing
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "Adding probability to testing: Golang"
|
||||
date: 2019-05-23T17:17:17-04:00
|
||||
draft: false
|
||||
tags: ["go", "testing"]
|
||||
---
|
||||
|
||||
In my work as a Software Engineer at Kickback Rewards Systems, I recently wrote an application that essentially reads packets from a TCP stream.
|
||||
It's amazing to me that writing a concurrent application that reads bytes from a TCP stream is SO EASY with Go, in less than 150 LOC!
|
||||
We've had similar applications in C and Python that are much larger than that at my company!
|
||||
Anyway, this application merely needed to mock the functionality of the production system, so that we could test the transmission of a new packet
|
||||
type from our client code. The client expected some entity acting as the server in a socket connection that would first read bytes from the stream
|
||||
to determine the length of the payload, then the payload itself. The server has to respond to the client in a similar manner, length preamble, and
|
||||
payload of response.
|
||||
|
||||
An interesting requirement arose amid development of this mock server. The requirement was that the server should simulate an actual internet connection,
|
||||
sometimes terminating the client's connection
|
||||
too early, sometimes delaying a response to the client, and sometimes just not responding at all. The trick to this though is that this wacky behavior
|
||||
needed to be inconsistent, something that happened seemingly randomly. To accomplish this, I developed a neat pattern using closures and function
|
||||
pointers.
|
||||
|
||||
Enter our goroutine, which is obviously not my real production code:
|
||||
|
||||
```go
|
||||
// Main and rest of program not included, it's not important to this example.
|
||||
|
||||
// Our goroutine.. called in the main socket listening loop
|
||||
func handleConn(conn net.Conn) {
|
||||
// Do some work and respond.
|
||||
}
|
||||
```
|
||||
|
||||
This is a pretty standard pattern, develop a function intended to be used as a goroutine, and call it in some loop passing a connection from a successful
|
||||
`net.Listener.Accept()` call. What I did to add a little chaos to this function, was utilize a little bit of closure trickery >:). To introduce any
|
||||
amount of chaos to your application, a function like this will do:
|
||||
|
||||
```go
|
||||
// Flip a coin, and do the action on tails...
|
||||
func chaos(action func()) {
|
||||
if rand.Intn(10) > 5 {
|
||||
// Executing our function pointer.
|
||||
action()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
On its own, this function may seem harmless. One may ask why you would want to flip a coin then do something if we achieve a certain result. The answer
|
||||
to that is simple, and that's where closures come in. Let's modify my goroutine from before to do something nasty.
|
||||
|
||||
```go
|
||||
func handleConn(conn net.Conn) {
|
||||
// Now let's introduce some... chaos
|
||||
chaos(func() {
|
||||
fmt.Println("Oh no, something bad happened!")
|
||||
conn.Close()
|
||||
|
||||
// don't worry about this.
|
||||
runtime.Goexit()
|
||||
})
|
||||
|
||||
fmt.Println("Phew, nothing's gonna happen, let's continue.")
|
||||
// Do the work.
|
||||
}
|
||||
```
|
||||
|
||||
The way this works is simple, we pass an anonymous function as the argument to chaos, noted above as type `func()`. This anonymous function that we
|
||||
create has access to any variables defined in its enclosing scope. `conn`, a function parameter, is defined in the same scope as the anonymous function,
|
||||
thus it is perfectly valid to reference it from within the function. Now, when the function is executed in `chaos` like this: `action()`, it is still
|
||||
holding a valid reference to the conn object, resulting in `conn.Close()` being called terminating the client's connection with the server. Use this
|
||||
pattern any time you would like to introduce a little bit of chaos to your testing.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user