Should your tests mock outside services or not? I keep seeing discussions on this topic. On the one hand, it lets your tests be in control of the testing context. Otherwise it may be very difficult to create a reliable automated test.
- The external service might return different results at different times.
- The external service might be slow to respond.
- Using the external service might require running the test in a particular environment.
- It may be impossible to generate certain error conditions with the real service.
- There may be side-effects of using the real service.
On the other hand, our mock of the external service might differ from it in important ways.
- Our mock service might have a slightly different interface than the real one.
- Our mock service might accept slightly different parameters than the real one.
- Our mock service might return slightly different results than the real one.
- The real service might change behavior, and we won’t notice until we deploy to production.
This leaves us in a quandary. What to do? Read More
At various times, the behavior of Facebook reveals to me that it’s a loosely coupled system with long latencies in synchronizing its various data stores. Just today, the iOS app asked me to confirm a friend request. When I did so, it gave me a pretty generic error—something about not being able to do this action at this time. That’s when I realized that I’d already accepted that particular friend request, days ago. That tells me that there are often inconsistencies between the database used with one interaction, and the one used with another interaction on another interface.
It also tells me that there are likely teams working on components, rather than features. If it was a feature team, the need for an error message would have driven a different underlying implementation. A feature team would want to differentiate between not being able to accomplish an action because it was already done, and because some problem was blocking it. For the former, there’s no need to show an error at all. Read More
A programmer, writing some new code, looks into some existing code that she needs to use. Something doesn’t look quite right. In fact, there’s a bug. Whether no one’s triggered it, or they have but their complaints haven’t reached anyone who will do something about it, is hard to say. Can she fix this code now and keep working? Or does something prevent that?
In such a situation, I would prefer to write a new test illustrating the bug, fix it, and check both the test and the fix into source control. This might take five minutes or an hour. It’s a small detour, but I feel better knowing that the code is now safer for the future.
Maybe, however, there are policies, either explicit or tacit, that prevent such quick resolution. Read More
I’ve been an advocate of using feature teams instead of component teams for a long time. Back in the 1980s and 1990s, when I was working on embedded systems using 8-bit microprocessors, I often did both the hardware and software development. The easiest way I found to work was to develop both in parallel, adding complete functionality feature by feature. Working in this fashion also gave me great insight into choosing whether to implement a particular bit of logic in the hardware or software. Sometimes a software implementation could save a lot of expensive hardware, and other times a small bit of hardware could save a lot of software or provide better performance. Since I was working on both sides of the fence, I could easily move the logic from one to the other, sometimes changing my mind as I learned new information while implementing the functionality.
When working in larger teams, the work was usually split between hardware engineers and programmers. Occasionally I could influence a change in the hardware while working as a programmer, but not always. I really missed the ability to optimize across the hardware/software interface that I’d had when doing both sides.
When you scale up to large numbers of people working on a system, things can get out of hand, though. What happens when a lot of people make changes to the same components without talking with each other? Usually, the conceptual integrity goes out the window. So, what to do? Read More
There are many times where a call to get some data is time-consuming or expensive. Perhaps it makes a webservice call, or a network connection to a database, or iterates over a large collection to perform a calculation. If the values isn’t going to change rapidly, and might be needed again soon, it’s natural to want to save the value for that use. Read More
I see many discussions get heated or go in circles when the topic is “design.” In the field of software development, what is design? Read More
In my previous post, Avoiding Iteration Zero, I suggested starting with “the one obvious thing that needs to be done? (Hint: it’s not ‘login.’)” As Jon Kern has recently mentioned, this same topic has come up elsewhere. I was also in that list discussion.
Jon is, of course, right in a narrow sense. You can start with login, if you want. You can also start with an Iteration Zero. (Or, an Iteration Minus One, as I’ve seen one organization do when their list of pre-planning outgrew one iteration.) I’ve observed that you can generally get better software, faster if you start somewhere else.
There are some very good reasons for this. Read More
Much of the time, the test-driven development yahoogroup is pretty quiet, but it has recently awakened from winter hibernation. The question “Is it OK to add code to a class only to improve its testability?” stirred up a wide-ranging discussion that brought in the topic of what constitutes good design. “Uncle Bob” Martin drew a bold line in the sand with his comment,
One reasonable definition of good design is testability. It is hard to imagine a software system that is both testable and poorly designed. It is also hard to imagine a software system that is well designed but also untestable.
I greatly sympathize with this statement, though I wouldn’t go quite that far. I don’t think it is so hard to imagine code that is testable, but poorly designed. For a trivial counter-case, there could be rampant duplication of testable code. I would call that poorly designed, but it doesn’t affect it’s testability. Therefore I would soften Uncle Bob’s definition to “One reasonable component of the definition of good design is testability.”
To me, the notion of “testable code” is the same thing that “testable circuit” was back when I worked on a custom integrated circuit. Mostly, that depends on the ability to put the circuit or code into a known state, exercise it, and see the interactions with its collaborators and its resulting state. Read More
I’ve always been of the opinion that software construction is not like physical construction. Or, rather, that the construction part, analogous to hammering two-by-fours together when building a house, is running the compiler. And, of course, you rarely tear a house down and rebuild it over and over, until you get it right. Therefore, software development is nothing like mechanical construction.
Except, of course, when it’s very much like mechanical construction.