Effective Unit Tests
The value of Unit Tests is now well understood and appreciated by many developers. Yet, there’s a lack of literature on writing effective Unit Tests beyond the ‘Getting Started’ guides and beginner’s material. That’s what lead Jay Fields, Senior Software Engineer at DRW Trading, to write ‘Working Effectively with Unit Tests’. In this interview, we tap into Fields’ knowledge of Unit Testing and learn how to write tests that are maintainable and can be used by all team members, when to use TDD, the limits of DRY within tests and how to approach adding tests to legacy codebases.
*Read more interviews with some of the world’s best developers on ** or follow us on *
“It’s really tough”, Fields says. “I think you first have to start out asking yourself Why?” and this is “the first step I think a lot of developers don’t take”. What happens when “I come back to this test in a month, am I going to even understand what I’m looking at? If you ask yourself that, I think you start to write different tests. Then once you evolve past that, you’re really going to need to ask yourself, ‘If someone comes to this test for the first time… how long is it going to take for them to be productive with that test?’”. So ultimately, the real value is “a lot about establishing patterns within your tests that are focused on team value and on maintenance of existing tests, instead of being focused on getting you to your immediate goal,” says Fields.
It’s this focus on the team and the longevity of tests that leads Fields to consider DRY to be something on an Anti-Pattern. “It’s a tradeoff. I think people don’t recognize that nearly enough”. This trade-off comes in its maintainability, says Fields. “Imagine you have some junior guy, just graduated, fantastic programmer. He’s just not really familiar with xUnit frameworks that much. Maybe he doesn’t know that he needs to look for a setup method, so he’s basically stuck.” So “DRY’s great. If you can apply DRY on a local scale within a test. It’s fantastic if you can apply it on a global scale, or across the whole suite so that everybody’s familiar with whatever you’re doing then that’s great too. That helps the team, but if you’re saying that this group of tests within this trial behave differently than these tests up here, you’re starting to confuse people. You’re taking away some maintainability.”
Another concern for Fields is the value derived by adding tests. He’s a proponent of the selective use of Test-Driven Development (TDD). “Everybody needs to try TDD,” says Fields. “Just try it all the time. Try to do it 100% of the time, and I think you’ll very likely find that it’s extremely helpful for some scenarios, and not for others… For me personally, I’ve found when I know what I want to do, if I have a pretty mature idea of what needs to happen, then TDD is fantastic because I can write out what I expect, and then I can make the code work… The opposite scenario where I find it less helpful is when I’m not quite sure what I want, so writing out what I want is going to be hard and probably wrong. I find myself in what I think is kind of a wasted cycle of writing the wrong thing, making the code do the wrong thing, realizing it’s wrong, writing the new thing that I expect which is probably also wrong, making code do that, and then repeating that over and over”. When you get into a loop like that, you need to stop says Fields. “Just brainstorm, or play around with the code a little bit, and then see what the test could look like once you have a good idea of the direction it is going.”
This focus on value and the return on investment is especially important when adding tests to a legacy codebase. Fields asks, “do you really need to test everything equally when the business value is not the same? … There are so many things that aren’t really important to you. As long as you can keep sending them bills and keep getting paid and find the customer when you need to, the rest of the stuff is not as important”. So to guide his decision making he suggests that you “focus first on the things that are mission critical. If the software can’t succeed without a function working correctly, or a method working correctly, then you probably need some tests around that. After that, you start to do tradeoffs”. It’s important to take a holistic view, as “you are then maintaining the code and the tests”, says Fields. “If I have too many tests, then I need to delete some because I’m spending too much time maintaining tests that aren’t helping me. If I don’t have enough tests, then it’s very simple, just start to write some more… I tend to do that whenever a bug comes in for something that’s critical.” For everything else it’s important to ask ‘what is the real impact here?’ before diving in and writing a test for it.
Another false errand can be the elusive 100% test coverage. “Earlier on I thought it was a good idea… then you start to realize you need to test things like, say you’re writing in C#, and you have to automatically generate a set. Do you really want to test that? I think all of us trust that C# is not going to break that functionality in the core language, but if you put that field in there, then you have to test it if you want to give 100% coverage. I think there will be cases where you would actually want to do that… maybe you’re writing for NASA or maybe you’re writing for a hospital system — something where if it goes wrong, it’s catastrophic”. However, few of us find ourselves writing such code. So for the rest of us, Fields suggests that if you’re just “building some web 2.0 start up, not even sure if the company is going to be around in a month, and you’re writing a Rails app, do you really want to test the way Rails internals work? Because you have to, if you want 100% coverage. If you start testing the way Rails internals work, you may never get the product out there and you’ll have a great test suite for when your company runs out of money”.
When it comes to making mistakes with Unit Tests, Fields says, “I think the biggest one is just not considering the rest of the team.” “It’s really easy to do TDD. You write a test, and then you write the associated code, and you just stop there… I think the largest mistake people make is they don’t think about the most junior member of the team… Are they going to be able to look at this test and figure out where to go from there? And if the answer’s no, then that might not be the best test you could write for the code”. But it’s difficult to learn more beyond this, says Fields. “I think that there are great books that help you get started. I think ‘The Art of Unit Testing’ is a great book to help you get started. There’s the ‘xUnit Patterns’ book, that’s really good”. But, beyond that “I really think that there’s room for some new books… because Unit Testing’s only going to be more important. It’s not going away”.
*Read more interviews with some of the world’s best developers on ** or follow us on *