Cleaning Up Your Code: Testing
Aaroh Mankad
Posted on January 3, 2018
This is an excerpt from my article "Cleaning Up Your Code" on Medium. I'll be publishing each section daily, read ahead on Medium!
Before I start explaining the specifics of testing in certain languages, I want to start by explaining the concept of Test Driven Development. You may have heard the term before but thought that your project wasn’t big enough to adopt this method of developing.
Test Driven Development (TDD) is a method of developing in which you write tests before you write any code. Why would you want to do this?
TDD actually enforces writing tests for your code! Many times you’ll write a program, run some examples and call it done. However, you’ve likely missed several bugs in your code by testing only the optimal cases. If you did manage to find some bugs and fix them, it is more work to make sure your fix did not break any previous cases. For this reason, tests serve as a method of documentation for all the cases you’ve checked so far and as a way to force you to think of ways to break your code.
So what makes a good test? Follow the F.I.R.S.T method!
- Fast: If a test is slow, your approach to the task is probably not optimal. If the test is slow for a sample size of one, imagine how that would scale to a system of a million users!
- Independent: A test should not depend on the outcome of another test. In the case that tests were dependent, then your tests would fall in a domino effect, hiding bugs in a different test until you fix the first bug. (Not ideal for when you’re working in teams asynchronously.)
- Repeatable: A test should not leave anything to chance. Every time you run a test, assuming no changes to the code have been made, you should expect an identical outcome. If the test cannot be repeated, you will eventually fall into a “It works on my machine!” attitude which is not beneficial to your team or your users.
-
Self-Validating: A test should return a boolean, true or false. This allows us to automate the job of testing our code, checking if all the tests return true or not. If you have to check some printed output to see if it matches the expected output, you could instead do a diff on the expected string and the returned string. (This changes our test from a
string
return type to abool
return type!) - Timely : This is the self-fulfilling prophecy in F.I.R.S.T. If you write your tests in a timely manner, your testing will go smoothly. Do not put off writing tests in favor of extending the functionality of your program.
Some housekeeping notes, but keep your tests separate from your actual program logic, ideally in a /tests
directory.
Testing can be very domain-specific in its scope. My experience is primarily in Javascript and C++, which I feel represent two very different sides of programming. (Hopefully your language of choice falls somewhere on that spectrum!)
Javascript
Before you start thinking that tests have to be this fancy concept of your code that can only take a significant portion of your development time, remember that a test can be as simple as checking the output of a function to the expected output!
There are more robust ways to write tests in Javascript, mostly due to the vibrant developer community. Most commonly used is the test runner. A test runner will automatically run all your tests and present them in a very clean format. It is also much more extensible in the use case that you have databases of objects and want to mock some test data for your tests.
The test runner that I’m going to recommend is Jest. Jest is used by Facebook to test all their JavaScript code including their React applications. To me, Jest has a lot of reputation from being backed by Facebook and having a team dedicated to its active development.
To learn how to start using Jest, I highly recommend Fun Fun Function:
C++
As in Javascript, sometimes the easiest way to test your code in C++ is with some assert statements.
When you want to get into some more complex testing, such as checking for memory leaks, you may want to start using static analyzers. First, use cppcheck to see if you even have a memory leak in the first place. cppcheck can also offer advice for better performance or cleaner code style.
If you do end up having a memory leak or segmentation fault, one option is to compile your program with the -g flag for debugging support. Then you can run your program with GDB (GNU DeBugger). GDB allows you to set breakpoints in your code, step through functions, inspect values at each step, and locate segmentation faults.
Fun fact: You can also use GDB as a disassembler for executables!
Thanks for reading! How do you test your code?
Posted on January 3, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.