I like tools. I'm old enough to remember what it was like to develop software with a simple text editor (VI, anyone? Emacs? How about See? I'll bet nobody remembers See.exe…) Then you'd compile it at the command line, manually run the linker, etc… and I appreciate how much our tools have improved over time. I love intellisense, source trees, version control, context highlighting, the wonderful way resharper allows you to fix problems en-situ, and all that.
But I also know that the more powerful your tools are, the more you can become dependent on them and, worse, the easier it is to misuse them.
A good example of this is the use of automatic mocking tools like NMock, Easymock, Rhinomocks, and the like. I'm not focusing here on a specific tool or approach, but these are frameworks which are capable of generating your mock objects for you. The dynamic tools (my preference) can actually do this at runtime, while a given test is running, requiring no additional source code in your project. Nice.
They also make it easy to create a mock that is both conditionable and inspectable. A conditionable mock is one that the test can "program" to return different values. An inspectable mock is one that will "remember" what happens to it during the test so that the test can verify that it was the right stuff. If you are unfamiliar with mocking, you can visit the mock object page at our pattern repository for more details and examples.
But… just because you can do something does not mean you should.
Let's say object O, if operating properly, should produce value V when given parameter P. A typical unit test would create an instance of O, pass it P and assert that V was returned. Now let's further stipulate that object O, in order to fulfill this requirement, interacts with another object, which we will call S (for service). Using a dynamic mocking tool we could, if we like, mock out S and have the mock record the interaction between O and S, then the test could inspect the mock afterward to ensure that the interaction was what the test expects.
You can do this with hand-crafted mocks, but the tools make it so easy to do. Usually this is just a single method call on the mock's control… something like: control.verify();
Sometimes the interaction between objects and systems ("workflow") is specified by requirements, and as such you'd expect a test to verify it. But very often a given workflow is simply the implementation the developer has chosen, and might be refactored at some point into a better or more advantageous implementation. When we refactor, we are not changing behavior, and we expect our tests to continue to pass as a guarantee that we're doing what we expect; refactoring and not enhancing, changing, or decaying the behavior of the system.
In other words, perhaps the test above should simply verify that O, given P, returns V, and not how it manages to accomplish this. Perhaps we should be free to change how it does so without causing the test to fail. If the test verifies that O interacts with S in a specified way, we would not be free to change this in refactoring.
I'm not saying either thing is right in all cases, I am saying that the tool makes it easy to ignore the question, and to verify many workflows that should not be part of testing. This tightly couples tests to implementation and makes refactoring very difficult.
I'm not picking on mocking tools here, this is just an example. Tools that speed up reflection (and thus make it easy to break encapsulation on types), clever techniques for testing private methods on classes, etc… these all add power but potentially cause us to avoid thinking things through.
When I started woodworking, I decided not to buy a table saw. I cut wood using a Japanese woodworker's draw-saw. It takes longer (much longer), but in order for me to harm myself with my draw-saw it would literally take the kind of effort the guy in "127 Hours" put into removing his arm. With a table saw I could dismember myself effortlessly in seconds.
Being a good developer is about your brain, not your tools. I'm not saying you should avoid powerful tools (though I still don't own a table saw), but that you should not allow the power they give you to cause you to stop thinking.
As usual, I encourage any comments or thoughts at our Lean Programming Group yahoo forum.