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
stringreturn type to a
- 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
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!
To learn how to start using Jest, I highly recommend Fun Fun Function:
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?