How to Love Writing Tests

David Zhang
3 min readOct 5, 2020

I’ve heard many developers say they “don’t have time to write tests.” But most of the time that’s a fallacy, because not writing tests is how you end up with no time. Lack of testing means you have to spend precious time manually testing features every single time something changes. I believe the real reason a lot of developers don’t write tests is because they make testing hard for themselves. Let me show you what I mean.

Here’s a typical function that you might write. It’s a common and simple workflow where you fetch some data (from an API, a database, etc…), do some calculations on it, format the output, and return it.

function returnDataToUser
// fetch some data
...
// do some calculation
...
// format the return values
...
return result

Now how might we go about implementing the logic here? The easy thing to do is to just shove all the logic into this function. It’s easy to understand and simple right?

function returnDataToUser
// fetch some data
response = api.get('/some/data')
thingICareAbout = response.thatThingICareAbout
// do some calculation (business logic)
for (thing in thingICareAbout) {
...
}
// format the return values
result = {
myKey1: thingICareAbout.someOtherKey1,
myKey2: thingICareAbout.someOtherKey2
}

return result

Now it’s time to run the code and… there’s a bug. Now to debug your code you will need to add print statements up and down the function until you figure out where the error occurs. Maybe the response format isn’t as you expected? Maybe your calculation logic has some edge case you didn’t think about? Maybe you aren’t returning the expected result?

There’s a lot that can be done to remediate these issues, such as using static typing, static analysis, linting, etc… But in the case of logical errors there isn’t much better we can do than testing!

There are many kinds of testing but I will keep it simple and focus on unit and integration tests.

If we want to test our function in it’s current state we can do so in a few ways. We can directly test the function, resulting in the API call and any other side-effects. This works if the side-effects don’t matter, but in many cases you want to avoid tests with side-effects, because state outside of your function is not dependable and often difficult to reproduce! So to remove side-effects you might mock out any side-effects before you test. This is fine if the logic here is truly simple, but in many cases you would be better off writing your code in a way that lends itself to easier testing.

In the above example many developers might not write tests because they’ve made testing hard for themselves. But I’m here to tell you that testing doesn’t have to be difficult, it can be easy and satisfying. Let’s see how we could rewrite the code to be more test friendly.

function returnDataToUser
// fetch some data
thingICareAbout = fetchSomeData()
// do some calculation (business logic)
thingICareAbout = doSomeCalculation(thingICareAbout)
// format the return values
result = {
myKey1: thingICareAbout.someOtherKey1,
myKey2: thingICareAbout.someOtherKey2
}

return result
function fetchSomeData
response = api.get('/some/data')
return response.thatThingICareAbout
function doSomeCalculation
for (thing in thingICareAbout) {
...
}
return thingICareAbout

Ok so we split up the function into smaller functions, so what? The point here is not that we need to split up functions into smaller ones, the point is to split functions up into testable ones. We see that we put all the side-effect logic into fetchSomeData which we can now easily integration test! If the return format ever changes or returns something unexpected the integration test can catch it! We also put all our business logic into doSomeCalculation which is now free of side-effects! We can independently unit test it and check for many edge cases that may have been difficult to test for as part of a bigger function! The best part? We didn't have to do any unnecessary mocking or write more code! Our tests are simpler and our functions are smaller and easier to reason with! 🥂🎉🥳

--

--