*I mean beginner programmers
As a coding newbie, you’ve surely encountered tests. Online coding programs like Codecademy and FreeCodeCamp rely on tests to check users’ input against expected outcomes. There’s nothing more satisfying than having someone (or something) else verify that your code is working correctly. Tests are awesome! They provide feedback and help guide our progress toward an eventual goal. Unfortunately, the real world doesn’t come pre-equipped with tests.
TDD, or test-driven development, is a process that involves creating tests before writing any code. The idea is to think critically about the goal of the code before putting pen to paper (err…hand to keyboard). Having a clear idea about what a function should do makes it much easier to correctly write it out.
TDD is an iterative process. It starts with writing a test, which is initially failing because (duh) no other code has been written to get it passing. The next step is to code out the minimum viable solution to the test at hand. Don’t go above and beyond — just get the test to pass. Once one test is passing, add another and adjust your code to make the next test pass, etc. By following this method, you’ll ensure that you are catching all bugs early on.
Let’s try an example using a programming classic — FizzBuzz. In our version, we’ll write a function to take a number as an argument and output a string that says “Your number is [number]”. The catch: if the number is divisible by 3, replace the number with “Fizz”; if the number is divisible by 5, replace it with“Buzz”; if the number is divisible by 3 AND 5, replace it with “FizzBuzz”. We’ll tackle this using TDD.
Next we will put together our tests. Using the assertion function above, we’ll need our ‘actual’ value (obtained from calling the function), the expected return value of the function, and the “testName” which is a simple description of the case we are trying to test. When creating test cases, think of potential edge cases and be sure to include those. What should happen when “0” or “nil” is passed in? Your code will be based on these tests, so write the tests with this in mind.
Ok, we’ve got our tests and are ready to take a stab at a solution. Remember that we told our assertEqual function to log “passed” to the console if a test is passed, otherwise to log an error message. I’ll write out an incorrect solution first to see how that looks.
Running the function with the tests written above, we get…
Saaaad. But, luckily, we wrote our tests in a way that make it easy to find where we went wrong. Given the test name, “should print ‘FizzBuzz’ if divisible by 3 & 5”, it’s clear that the problem occurred in the case where the number was divisible by both 3 and 5. We can also compare the expected and actual outcomes to see where we went wrong. Let’s fix that…
Using the test as a guide, we were quickly able to find the error in our logic. Admittedly, this is a contrived example and may seem like common sense. But the intention is to draw attention to the importance of thorough testing. In small projects it’s easy to get away with writing code and hoping for the best, but at large scale, iterative testing is imperative to catching bugs before they manifest into bigger issues.
Types of Tests
Testing is a major part of programming, and there are individuals and entire teams dedicating to testing out code. Not surprisingly, tests are done at various levels throughout development. The FizzBuzz example described above could be described as a unit test. It’s the simplest type of testing performed at the smallest scale — a single unit or group of related units (a single function, for example). A developer would likely write unit tests before pushing code to the rest of the team to ensure that her individual contributions are bug-free.
There are several other types of tests. Integration tests, not surprisingly, look at the integration of several different modules or units to ensure that they work seamlessly together. End-to-end tests focus on user experience and flow. Performance tests examine how a product performs under significant load. Smoke tests ensure that the most basic functions of a program are working properly. And there are more where those came from!
Some tests are more costly to run than others, and there are certainly programs that handle a lot of the heavy-lifting with automated testing. As with most programming concepts, however, it’s important to have a basic understanding of what happens under the hood and why.
TLDR: “If it’s worth building, it’s worth testing. If it’s not worth testing, why are you wasting your time working on it?” — Scott Ambler, Enterprise Agile Coach
TDD alternatives volunteered by disgruntled StackOverflow users (https://stackoverflow.com/questions/3144173)
Cowboy Coding (Since my code is never at fault, I don’t need to test it thoroughly, if at all.)
MaDD — Manager Driven Development (It already takes you longer than you estimated just to code the real product — now you want to spend more time writing tests which never get released?!?!)
AD(D)D — Attention Deficit (Driven) Development (Randomly work on whichever portion of the application attracts your attention at the time.)
FDD — Faith Driven Development (Because you need to pray your project works on every release.)