Tuesday, 8 September 2009

Design for Testability

      • An object uses a static class e.g. using Util.GetProduct(id) inside Product class! - use instance class only.
      • An object uses (HAS) other objects; e.g. an Order object uses one or more OrderItem objects - use dependency injection.
      • Global state e.g. Singleton classes - use an IoC container.
      • An object uses a class from a third-party or another assembly to whose implementation we don't have access such as Order object uses ConfigurationManager - Use Adapter pattern.
      Design tips:
      • Avoid static classes. Create instance classes instead and use an IoC container, such as Unity, to access to that singleton instance.
      • Don't instantiate dependent classes inside another class; instead use constructor injection always.
      • Make sure that you have an interface for each class of your application to enable dependency injection using either constructor or property injection
      • If ClassA uses ClassB, who should instantiate ClassB? Allow ClassB instance to be injectable using the constructor using constructor injection.
      • Let your constructors do only assigning fields; don't call any methods in the constructor, etc.
      • Use an IoC container to manage how your objects and dependencies are instantiated.
      • If your class depends on runtime-determined parameters, use Abstract Factory pattern to create that class. Simply use an IoC container to instantiate that Factory class. Avoid using Initialize() methods in the class which has dependencies for 2 reasons: 1) developer can call it twice or not calling it. Using Abstract Factory makes sure that your class is initialized always properly.
      • If ClassA is using ClassB and ClassB is a singleton class, again you can still create an interface of ClassB and inject it to ClassA.
      • Avoid manual singleton classes. If you’d need a singleton, use the IoC Container and let it control the lifetime
      Should I be writing unit tests for Private Methods?
      There are 3 approaches you can take with private methods:
      1. No unit tests for private methods. Instead leaving them be tested when other public methods that use them are tested.
      2. Use Internal: write unit tests by making those members "internal" rather than "private". Configure the app so that the "InternalsVisibleTo" allows the unit testing project to access the internal members.
      3. Use reflection to write unit tests for the private members
      I'd use the combination of the above approaches usually depending on the scenario and whether I'd really want to test a private method or not.


      No comments: