I’m working on a Ruby on Rails application, where automated testing is an absolute must. Unlike Java or C++, you don’t have any kind of useful compile-time warnings and errors to tell you where you’ve screwed up, made a typo, etc. Most errors in a Rails web app happen when a user interacts with your code, so if you don’t test, you don’t know about bugs until it’s too late. This is one of many reasons I’m wishing for a nice C++ project to play with… but that’s another topic for another day….
So anyway, I’m trying to do more BDD (“Behavior-driven development”) in my day-to-day work lately. Write the tests based on the desired behaviors, then figure out how to make the code do its thing. Most of the time I do the tests wrong, and need to fix them, but I’m slowly getting better. The key, though, is that I end up thinking more about how I want to use code than how I want to write a class. This is important, especially when building a small piece of a large app, because oftentimes we developers will build a class with a million use-cases and cool features, only to find that 90% of them were wrong, and the other 10% work, but not necessarily in the easiest or most intuitive way. Building a class by itself is generally not as good as figuring out a nice interface for a specific problem, and then coding the class to conform to that interface.
So BDD is super-cool. Great. But how did this save me? Well, it turns out I’m a moron. I wrote a bunch of tests and code and committed stuff to git using the best-practices that escape 99% of all devs, which perhaps I’ll rant about another time… but really, is it so hard to understand the concept of having atomic, self-contained commits with good commit messages?
To avoid another tangent, I’ll get to the point: I forgot to commit a new file I had added. Tests passed, because the file was in my working directory, but the file was never a part of the repository.
Damn it, one more tangent. I don’t often use
git add --all or
git commit -am or any of that bullshit. Idiots do that because they don’t understand the value of atomic commits, cherry-picking, reverting commits with as few side-effects as possible, or
git bisect. (Side note: yes, this is definitely a topic that deserves a longer rant.) Learn to use your tools and stop treating git like subversion’s big brother. My default workflow involves a ton of
git add -p and
git add path/to/new/file. And yes, it’s all command-line, no GUI. And yes, I’m still probably faster than you and your commit-bombs because of the time I save tracking down problems. I’m also a better developer. And smarter than you (I’m writing garbage, but you’re reading it).
Back on track: tests passed but the file wasn’t in the repo. Being the git pro that I am, I switched to another branch because I’m working on several features at once, and keeping them all in sync via rebase as they merge. Because as anybody sane knows, a five-commit branch with a dozen merges is stupid (yes, I’m looking at you mercurial lovers out there).
So as I’m jumping from feature to feature, at some point there’s an untracked file that I know is part of a different branch. I absentmindedly remove it, because after all, I know it’s part of another branch (and I did mention I’m a moron). A week or so later (today), I realize the mistake as I’m back to working on this branch. It wasn’t a lot of code, but I can’t remember how it worked. I can’t even remember what it did at first, I just know I lost it and tests are failing.
Looking at what failed and what my tests expect, I’ve finished fixing it and re-run tests on all changes throughout the branch in under an hour. Without those tests, I’d have probably had to re-read tickets, look for notes I wrote about said tickets, etc. It probably would have been 3-4 hours of work and may not have actually behaved the same as my original code. My new code, on the other hand, definitely behaves the way it needs to.
As much as I despise the fact that Rails forces very heavy testing, these particular tests were a very good thing today.