It’s easy to overlook the importance of unit testing. Writing tests can be tedious. They must be updated constantly as code is refactored, and when you have a large code base, you may have to write many tests to even come close to testing all cases. Despite this, unit testing is a necessary part of creating clean, working code. One way to make the testing process easier is through the use of mocks. Read more on Intro to Mocking with Moq…
I remember one of my CS professors at Calvin starting class one day by talking about TDD (Test Driven Development). “Write your tests first, then write the code to make the tests pass,” he said.
At that point in my programming career, TDD and unit testing in general just seemed like extra boilerplate code required to make my professor happy. If my assignment didn’t have tests (or good enough tests), I got a bad grade. However, I rarely, if ever, actually followed TDD principles when I was on my own—it just didn’t make sense to me. Write the code, test it manually, and then solidify it with some automated tests—that sounded like a better use of my time. Read more on How I Learned to Love TDD…
UPDATE: Justin Searls, the author of testdouble.js sent me an email with some notes on this post. I’ve added some his comments below to provide some additional context.
Unit testing is a great way to help ensure the continuous delivery of working code over a product’s lifecycle. In client-side applications, unit testing data retrieval is especially important since using data from these asynchronous calls is at the core of what most of these apps do.
Unfortunately, asynchronous calls aren’t easy to test. Read more on Unit Testing Frontend Network Requests…
How far should we take unit testing? Should every line of code be covered by a unit test? What about code that’s hard to test? Let’s look at the cost and value of unit testing in a couple of different situations.
Tests We Can All Agree On
It’s easy to see the value of unit tests when we’re writing actual functions. Let’s say we’re writing a helper function called
next_valentines_day for our greeting card application. Here are some tests that we might write:
describe next_valentines_day do it "returns next year's valentine's day when after Feb 14" do expect(next_valentines_day('2014-02-15')).to eq('2015-02-14') end it "returns same year's valentine's day when on Feb 14" do expect(next_valentines_day('2014-02-14')).to eq('2014-02-14') end end
Even from just these two tests, we we’ve significantly reduced the risk of calculating the wrong Valentine’s day in our software. And that means we’re less likely to lose valuable time fixing bugs in the code or dealing with other fallout from the code being wrong (like decreased greeting card sales). Read more on The Cost of Unit Testing…
In my last post, I argued that “untestable code” is code that cannot be efficiently unit tested. Today I’d like to walk through my top five anti-patterns that make writing unit tests painful. If you aren’t in the habit of writing unit tests, hopefully these tips will help you build more testable code and see how test-driven development can help drive clean design.
1. Lack of Dependency Injection
Let’s start by considering a worst-case single line method:
class CrisisHandler def code_one MissileControl.new.launch_nukes end end
What’s so bad about this code? Well, let’s assume that this is a pretty important feature to get right, and that your code had better work as expected. We definitely want to be able to validate this method. Read more on This Code Is Untestable! (Part 2, for Developers)…
Let’s say you’ve been given ownership of a software component, and you’ve found that the quality is not where you need it to be. The defect backlog is a mile long, and every bug fix or new feature your team implements creates regressions.
What do you do? Well, you ask your development team to write a test plan to flush out remaining defects and ensure that you don’t run into more regressions. Problem solved! That is, until your development team comes back with those terrible words: “This code is untestable.”
What does that even mean? Surely all code is testable? You can run it manually and validate that it works. Or you could write an automated suite that brings up the application, walks through your user stories, and makes sure that everything checks out. So what’s the problem? Read more on This Code Is Untestable! (Part 1, for Managers)…
Like many other Atoms, I’ve recently been doing some work with EmberJS. Ember is an awesome web development toolkit with some really killer features. One of my favorite parts of Ember is how easy it is to test. The framework comes bundled with a system testing framework, and its object model makes unit testing a breeze. Combine all that with a great test runner that has CI integration, and you have a really awesome testing ecosystem for your new app.
System Testing with Ember Testing and QUnit
Test-driven development is an essential discipline for any software craftsman. Similar to how accountants use double-entry bookkeeping as a safety net, software developers can use TDD as their safety net.
If you need a refresher on TDD — and what it is and isn’t — I’d suggest my post on why Simply Writing Tests Is Not Test-Driven Development.
1. Think of Tests as a Sum of 4 Parts
When writing unit tests, it helps to think of them as 4 parts — setup, invocation, assertion, and tear down — and to consider each step as a series of questions. I start with the invocation first. Read more on 4 Tips for Improving Test Quality…