We write a lot of unit tests at Hubstaff. Testing is something we have taken seriously from day one and as a result it’s a big part of our development culture. If you’ve been on the fence about writing unit tests but haven’t dove in headfirst yet let me tell you why you should. You’ll save yourself a bunch of pain and work happier by writing unit tests. Happy programmers write better code. There’s no better medicine for a software product than a frequent dose of high quality code.
Disclaimer: I’m not going to preach in this post about whether you should write tests before you write the code. That’s a debate in itself. I just recommend that you write them one way or another.
Power up your workday
Reach your goals faster with time tracking and work management.
Myths About Unit Tests
Most developers don’t write tests. It’s easy to understand why. Tests take time (and in most cases money) to write. Maybe your boss or client thinks that your progress will “slow down” by writing tests. Maybe your company doesn’t think the cost is justified. Unit tests do increase the size of the code base, which means more lines of code to maintain. Ugh, who wants that?
So you skip writing unit tests because it’s obvious that you’ll save your company money, finish your work early and make it home in time to watch Game of Thrones. Not so fast! Give me a chance to change your mind.
Unit Tests Explained in 60 Seconds or Less
For the uninitiated let’s quickly cover what unit tests are. Since I’m a programmer I’ll show you some code.
This is an RSpec test but most unit test frameworks look and work similarly. You write a bunch of these tests, which assert certain conditions. A test does X and expects Y. If the test does not get Y it fails. It really is that simple.
The Big 3 Reasons Why You Should Write Unit Tests
- Know if your code *really* works
- It will save you a lot of pain (and time)
- It makes deployments a snap
Know if your code *really* works
Unit tests aren’t a replacement for functional testing. But they are the solid foundation on which the rest of your testing process should be built.
Opening your app up and taking the new feature you just developed for a test drive is a good practice. This is a standard part of our testing process at Hubstaff. We don’t ship code if it hasn’t been functionally tested. I’d also recommend getting one or two other people to open the app and exercise that new feature too. But this sort of functional testing is far from ideal.
Functional testing is an imperfect way to test new code for several reasons.
- Usually you, the developer who wrote the code, performs the functional testing. You’re going to use the feature as expected since you designed it. But software usually breaks when you use it in unintended ways!
- Functional testing is really tedious. It requires you to think up ways to test the feature. If you are not the developer you probably have no clue where the fragility in the feature. That fragilityis what you should be focused on. For example, I’ll often times email the people who will be performing the functional testing a list of things to check because I know what code I changed and what it is likely to affect. That’s exactly what writing unit tests is all about!
- It is usually unscripted. So if you ever do regression testing this feature will be tested differently and almost certainly not as thoroughly the second time around. Conducting functional regression testing isn’t reasonable or a good use of time. Wouldn’t it be nice if you could automate it? Oh yea, you can, just write unit tests!
Without unit tests you really do not have a testing process. You have testing chaos. That chaos will result in a lot more time spent than writing unit tests.
It will save you a lot of pain (and time)
I don’t like interruptions when I’m working. Who does? So I’ll do just about anything to avoid them. One of the worst types of interruptions is when something breaks in production and I have to stop what I’m doing to put the fire out. Unit tests are one of the best ways to prevent this. You look at your code through a different lens when you are writing tests. This causes you to see the fragile parts a lot easier. Things that you would have missed before shipping the code to production are much more likely to be caught if you are taking the time to examine your code. With unit tests I stopped spending my time putting out fires and can focus almost all of my time on forward progress. Believe me, there’s nothing more your boss or client hates than getting emails from users about things blowing up in production. I have been a part of a few projects where it feels like each feature goes through a trial by fire. That is a very slow way to develop. It’s almost impossible to find the time to develop new features when you’re having to put out fires all day.
The unit tests are really just the documentation of how your code should behave. Some people will tell you that this documentation is valuable. I would agree. If you cannot tell from looking at the code what it is trying to do, which is a problem in itself, you at least have the unit tests to tell you the story. This is especially helpful if you are working with someone else’s code. But even if it’s your own, how well will you remember all the edge cases in six months?
Another often overlooked advantage of unit tests is being able to work through a problem faster. How many times have you been working on a feature and you go to test it in the app and you get an error message? Probably all the time unless you are a god among programmers in which case did I mention that we’re hiring? Think about how much time it takes to have to launch the app, try to do something, see that it breaks, look at the code, rinse and repeat? It’s much easier to just write a test that causes the error and tests for the successful condition. Then change your code and rerun the test. Your development cycle improved by an order of magnitude. You just leveled up!
It makes deployments a snap
Before I learned about continuous integration I thought I had a really simple deployment process.
My old process
- Run tests on feature branch
- Pull down latest changes from master on GitHub
- Merge feature branch into master
- Run tests on master
- Push code to a remote repository (either staging or production)
- Run database migrations
There are all sorts of things that can go wrong in this process. I could easily make a mistake and forget to run the tests on master after I merge in my feature branch. I could forget to pull down the latest code for master from the remote before performing my merge locally. Most of all though I would forget to run the database migrations after deploying the code to production – a lot. Life was not good.
Then I discovered continuous integration. Since we prefer using fully-managed, hosted solutions at Hubstaff we use CircleCI. There are a bunch of competitors but we found that CircleCI had the best grasp on the technical issues that need to be tackled well with any continuous integration service. My six step process above was reduced to one.
My new process
- Merge feature branch into master
With CircleCI all I have to do is review the pull request on GitHub and merge it. CircleCI integrates with GitHub so it already ran the tests on that branch when I pushed it to GitHub and tells me if they all passed. Once the merge occurs it runs all the tests on the new version of master. If those pass it pushes the code to staging and automatically runs the migrations.
To push code to production all I have to do is merge master into production and it takes care of the rest. If a test happens to fail the deployment is halted. Life is good.
But You Already Have Thousands of Lines of Code
If you’re convinced but already have thousands of lines of code in your code base I recommend the following strategy:
- Don’t ship any new features without writing unit tests for them first.
- These new features most likely rely on parts of existing code so write a few tests for that existing code with each new feature.
- If something breaks in production write a test for it at the time that you fix it.
Little by little you’ll increase your code coverage without grinding development to a halt.
How To Write Good Unit Tests
Here are some good resources on writing good unit tests. These are packed all kinds of good info. Though I recommend writing your first unit test as soon as possible. It’s like taking a bite of something you’re not sure about. Once you’ve tried it I guarantee it won’t be as bad as you thought.
- The Art of Unit Testing
- How to write “good” unit tests?
- Writing Great Unit Tests: Best and Worst Practices
Do you write unit tests? Why or why not? Sound off in the comments section below.
With Hubstaff you can track time, take screenshots, monitor activity levels, export reports and create invoices.
14 Day Free Trial!