Every few months the argument for and against the advance abilities of the Isolator mocking framework burst again. This time it started with this post: Test driven design – Willed vs. Forced Designs
Disclaimer: I used to work for Typemock company and was in charge for a long time over the development of the framework. so I claim no kind of objectivity .
the argument for and against usually circle around the following:
on one side, people claim that Isolator "breaks" the language barriers and, by allowing any kind of design to be implemented will end up in helping to build a poorly designed system.
on the other side, by allowing the freedom to design at will Isolator shift the responsibility back to the developer allowing him to choose the "best" design as he sees fit.
here are some points I want to comment on:
If you need Isolator you have a bad design
Actually that one is in most cases true. However the following claim "you have bad design" also holds for too many cases as well. Yes I'm saying that most systems out there are poorly designed. I'm also saying that most software project out there will fail. What I don't like about the initial claim is the conclusion that usually follows:
If you need isolator when you have poor design, usage of isolator will end up with a poor design.
That statement is plain wrong, you will end up with poor design unless you learn how to design better. The effect Isolator (or any tool for that matter) will have on a team design skills is minimal at best. Pragmatically speaking if you are are working on a legacy system isolator is probably the better choice no matter what. If you are working on a newly project and you just start out TDD, most likely using isolator will increase the chances you'll be able to stick with it. (adding the need to relearn design at this stage is soo much harder), and if you're working on a new project and you do know how to TDD. Then you should just know better and be able to safely use the tool. If you don't then you really do have a problem.
Isolator breaks the natural barriers of the language
Yes it does, but the statement "Statics are the death of testability" which actually means "don't use static methods" adds barriers to the language which is not there. so what's the better approach? again I don't know. It really depends on your personal preferences and has nothing to do with what is the best design
Actually there's no such thing is the best design. Every design must evolve always to fit the system needs.
Show me a concrete example
example number 1:
many systems have a dependency on the time of day. the naive way to approach that is to use DateTime.Now. but oops that one cant be faked (its a static method) making it a real pain to test. so experienced programmer introduce the ITime interface wrapping the system time with an interface and then adding some concrete code to make it work, all for the sake of testability. here's an example taken from the testify project on how to implement this:
public static class SystemClock {Please enlighten me as why this is better/simpler as opposed to use of DateTime.Now.
private static DateTime? fixedTime;
public static DateTime Now {
get {
if (fixedTime.HasValue)
return fixedTime.Value;
return DateTime.Now;
}
}
internal static void Set(DateTime value) {
fixedTime = value;
}
internal static void Reset() {
fixedTime = null;
}
}
example number 2:
I have a class A depends on Class B. Class B is the ONLY concrete class implementing the interface ISomeInterafce (and most likely this will hold true forever). Class A is the only place (for now and most likely forever) ISomeInterface is used. yes this is a simple example but since I'm inventing it, I get to set the rules.
and there are several ways to approach this:
1. Use some IOC container to instantiate B and send it to A. a valid OO way with many benefits however in my concrete example (and yes I'm the one setting the rules). I don't have a true need for a full pledged IOC yet, and this very simple scenario wont be the one causing me to start using an such a big hammer
2. Use a factory - a simpler version of the IOC container idea, much more light weight. but still at this point of time (and yes I get to make the rules) under the specified circumstance it falls under the YAGNI directive. If the situation changes (i.e. more places will use ISomeInterafce or more "kinds" of ISomeInterafce will evolve) a factory will be used but for now I don't really need that.
3. Instantiate B somewhere and pass it into A (the simplest form of DI) - a very simple solution which is valid under this scenario. the down side for it are , that it exposes some inner working details of A which may not be a good idea AND most likely it only shifts the problem to someplace else.
4. Just use a new statement somewhere inside A (lets say constructor) - I like this way. Its the simplest thing that can possibly work, and I'm not tied to the "design for testability" chains, so I can do that. and just before I get chopped, yes taken outside this context of this simple concrete example is most likely not a good strategy.
I want to make the Choices. I'm the professional trained to do them. I wont stand being TOLD how to do my job, not by a person, and especially not by a tool.