Net Objectives

Net Objectives
If you are interested in coaching or training in ATDD or TDD please click here.

Monday, February 20, 2012

Testing Best Practices: Test Categories, Part 3

download the podcast

Continued from Test Categories, Part 2

Constant Specification

We often find values that are significant to a given problem domain, but are otherwise arbitrary.  For example, the sales tax rate in a state might be .08 (8 percent), but this is just the rate as currently defined by that state’s government.

Similarly, we sometimes have enumerations that are also significant to a given problem domain.  In a baseball team, you have a Pitcher, Catcher, First Baseman, Second Baseman, Third Baseman, Shortstop, Left Fielder, Center Fielder, and Right Fielder.  Always these nine positions exist, but this is only because the rules of baseball say so.

Good coding practice says to avoid “magic numbers” in our code.  If we were developing a taxation application, we would not want to hard code .08 every time we needed the tax rate.  If we did, this would cause several problems:

  • It would be in more than one place (redundant) and thus if it changed would have to be changed in more than one place.
  • It would be meaningless to someone who did not happen to know it was a tax rate.
  • Other systems depending on the same value would likely hard code it as well.

Because of this we tend to create system constants to represent these values.  Something like System.TAX_RATE which, once established in a single place, can be used throughout the code in the place of the literal value.  Or, for an enumerated type, we create a discrete enumeration to represent it: Positions.PITCHER, Positions.CATCHER, and so forth.

This is good practice, and in the past we simply created these constants as needed.  In TDD, however, we have to look at the issue differently.

In TDD we do not create implementation code until we first have a failing test, and we always create only that code needed to make the test pass.  In TDD as we are defining it, the “test” is actually not a test at all, but a specification of the system.  Specifications must be complete.

In other words, we must create a test that specifies these constants should exist before we create the code that means they do exist.  This is extremely simple to do, but is very often left out of the process when people first start doing TDD.

For a constant:

public void specifyTaxConstant() {
 Assert.AreEqual(.08, System.TAX_RATE);
}

Not a tremendous amount of work here, just more the realization that it needs to be done.  With an enumeration, it might be a tad more involved:

public void specifyPlayerPositions() {
 string[] players = Enum.getValues(Positions);
 Assert.AreEqual(9, players.length());
 Assert.Contains(“PITCHER”, players);
 Assert.Contains(“CATCHER”, players);
 Assert.Contains(“FIRST_BASEMAN”, players);
 Assert.Contains(“SECOND_BASEMAN”, players);
 Assert.Contains(“THIRD_BASEMAN”, players);
 Assert.Contains(“SHORTSTOP”, players);
 Assert.Contains(“LEFT_FIELDER”, players);
 Assert.Contains(“CENTER_FIELDER”, players);
 Assert.Contains(“RIGHT_FIELDER”, players);
}

Why would we do this?  It does seems silly at first.  But, as easy as this is to to, it is actually pretty important.

  • This is s specification, remember.  In a traditional specification, these values would certainly be included.  In a tax application’s specification, you would certainly have, somewhere, “the tax rate is currently 8 percent” or words to that effect.  Or, “the positions in Baseball are:...”.
  • If a value needs to be changed (if, for example, the state raises the tax rate), then we want to make the change to the specification first, watch the test fail now, and then change the implementation to make the test pass again.  This ensures that the spec and the code are always in sync, and that we always have tests, going forward, that can fail.  It is quite easy, in test maintenance, to change a test in such a way as to make it impossible for it to fail (a bug, in other words).
  • If a value needs to be added (for example, Positions.DESIGNATED_HITTER) then, again, we have a place to change the spec first.
  • Other developers on other teams will now know about these constants, and will use them too instead of magic numbers.

So a constant specification, as simple as it is, tells you four very important things (espcially later, when reviewing the spec).

  1. That a constant was used
  2. Where it is
  3. What it’s called
  4. What its current values are

That’s a lot of value for very little work!  It is important to note, however, that all other tests that access these constant values must use the constants rather than the literal values.  Just like any code we have to maintain, we don’t want redundancies.

Creational

Instantiating objects is the first thing that we need to do in any system. After all, before we use them, they have to be there, right?
So what is there to test (that is, specify) about the creation of objects?

There are two scenario we need to deal with
  1. Simple, single objects
  2. Compound objects.

Simple Objects
When it comes to simple objects we need to specify two things:
  • The type of the object
  • Its observed initial state

What is a type (as in ‘the type of the object is …’)? The type is a concept that we have identified as existing in our domain. By the word concept we mean that we identify it but we have no idea how it is implemented. The concept could be a high level concept such as shape or vehicle, or a very specific concept such as an equilateral triangle or a 2005 Honda Accord.

In order to use an object it must be instantiated, and that is done through some factory mechanism. We may use a factory object, a factory method (such as GetInstance()) or the new operator (which is, after all, the most primitive factory method).

The simplest case
Lets assume we run a furniture business and in our catalog we have a desk named Aaarty,
AartyDesk is a low level concept in our model. AartyDesk will need to be created, and here is the test for it:

//If in our language the new operator cannot fail and still return (as is the case with C# or Java) then the test is:
public void testCreateAartyDesk{
 newAartyDesk();
}

//if the construction could fail and still return a value (as is the case with c++)
public void testCreateAartyDesk {
 Assert.NotNull(new AartyDesk());
}

This is not too interesting, but we have just specified that the concept of an Aarty desk exists in our domain and that it can be created. The next step, to make it more interesting (and useful) is to specify the behavior associated with AartyDesk immediately after it was created -- what we call the initial behavior of the object, which, naturally, depends on the initial state of the object. Note that we should have no visibility to that initial state, only the effect it has on the initial behavior.

public void testCreateAartyDesk {
 AartyDesk aartyDesk = new AartyDesk();
 Assert.False(aartyDesk.isLocked());
 Assert.True(aartyDesk.hasProtectiveCover());
}

Note that it is up to you to decide whether this initial behavior should be captured in single or multiple test; we will discuss that in a later blog.

At this point, we can note that the actual instantiation could vary, for example using a static factory method:

public void testCreateAartyDesk {
 AartyDesk aartyDesk = AartyDesk.GetInstance();
 Assert.False(aartyDesk.isLocked());
 Assert.True(aartyDesk.hasProtectiveCover());
}

or a factory object:

public void testCreateAartyDesk {
 AartyDesk aartyDesk = DeskFactory.GetInstance();
 Assert.False(aartyDesk.isLocked());
 Assert.True(aartyDesk.hasProtectiveCover());
}

The latter two options are significantly better from design perspective as they allow change to occur more easily by encapsulation the construction.

Creating a higher-level abstraction
Our domain analysis revealed that there will be more than one type of desk in our store -- we were told that a new model is being introduced. We want to capture this knowledge through the higher level Desk abstraction. There are several ways to capture this in the tests, depending on the creation mechanism. Firstly let's consider the new operator:

public void testCreateAartyDesk {
 Desk desk = new AartyDesk();
}

This simple test tells the reader that an AartyDesk behaves like a Desk. The same holds for if we use the GetInstance method.

public void testCreateAartyDesk {
 Desk desk = AartyDesk.GetInstance();
}

And the same for a factory object:

public void testCreateAartyDesk {
 Desk desk = DeskFactory.GetInstance();
}

But wait! How can we be sure that the DeskFactory.GetInstance() actually returns an AartyDesk? For that matter, how do we know that AartyDesk.GetInstance()actually returns an AartyDesk? We need to specify that in the test. For the purpose of this code we will assume the existence of an AssertOffType assertion.

public void testCreateAartyDesk {
 Desk desk = AartyDesk.GetInstance();
 AssertOfType<AartyDesk>(desk);
}
And the same for a factory object:
public void testCreateAartyDesk {
 Desk desk = DeskFactory.GetInstance();
 AssertOfType<AartyDesk>(desk);
}

Choice
Factories have two responsibilities. The obvious one is the creation of objects. Before this, however, is the responsibility of choosing which type to instantiate. In the example so far, there was only one type of desk -- Aarty, so there was no need to make any selection. What happens when we add the next variation -- Shmaag. Now the factory needs to choose between Aarty and Shmaag.

There are different mechanisms that can be used to enable factory selection. In our case we will have a helper class that returns the type of buyer: Cheap or Refined. Someone who knows the buyer category needs to set this information before the factory is called. In the test case, that someone is that test itself. We will also assume that the test used to specify these two categories of buyer via BuyerCategory was already written . We will also assume that the buyerHelper object was likewise already tested..

public void testCreateAartyDesk {
 BuyerHelper.getInstance().BuyerCategory = BuyerCategory.Cheap;//[4]
 Desk desk = DeskFactory.GetInstance();
 AssertOfType<AartyDesk>(desk);
}

public void testCreateAartyDesk {
 BuyerHelper.getInstance().BuyerCategory = BuyerCategory.Refined;
 Desk desk = DeskFactory.GetInstance();
 AssertOfType<ShmaagDesk>(desk);
}

As before, further tests, or assertions within these tests are needed to specify the initial behavior of these Desk objects.

Composition
The next level of complexity arrives with the introduction of collections. There are 2 things that need to be specified when collections are created:
  • The content of the collection
  • The behavior of the collection

Specifying the content of the collection is done by specifying what the expected values are and what their initial behavior is. For example, in a football team you have many players with specific roles. Consider part of the offense: Quarterback (QB), Center (C), Running Back (RB), Fullback (FB), Tight End (TE), Left and Right Tackles (LT, RT), Left and Right Guards (LG, RG) and Wide Receivers (WR).

The specific makeup of the offensive team depends on the specific play that is called.  For example, if we’re going to run the ball we may choose to have no WR and if we’re gonna pass it we may elect to have 3.

The OffenceFactory object will return the 11 players that should be on the field based on the type of play, which will be a parameter to the GetInstance method. We can have a whole playbook’s worth of plays (which need to be defined in their own tests, of course). Here is one possible test -- We assume here that Type is the type of the object and it can be passed around. There are many possible implementation in various languages. The code below is highly pseudocodish, but easily implementable...

public void testShotgunOffenceComposition() {
 Type[] expectedOffenseRoles = new Type[]
   {QB, C, LG, RG, LT, RT, TE, WR, WR, WR, WR}
 Player[] offense = OffenceFactory.GetInstance(Playbook.Shotgun);
 Type[] actualOffenseRoles = GetTypes(offense)
 expectedOffneseRoles.sort();
 actualOffenseRoles.sort();
 Assert.Equal(expectedOffenseRoles, actualOffenseRoles);  
}

The sort method sorts in place and the Assert.Equal when applied to collections uses iterators to iterate and compare the individual elements. The reason the collections are sorted before the comparison is to make is clear that the order does not matter, just the content.

Once we establish the role composition, we can iterate over the returned collection (offense in this case) and specify the initial behavior of all its contained Players.

Lastly, we need to specify the behavior of the collection as a collection. There are two ways to do that.

  • Assert that the type of the collection if some specific type. This ensures all the behavior implied by the type.
  • Specify the behavior of the iterators using ordering mocks. We will talk more about indexing mocks when we discuss mocks, but for now just remember that they are used to ensure that an iterator returns them in the correct order.


A Note on asserting the type of objects
Asserting types requires what may seem to some as an unsavory approach in some languages, which is the use of metadata; for example, .net reflection, Java introspection or c++ RTTI. In the context of asserting the type of objects, it is OK. As we have already discussed, the type of an object is not an implementation detail, but rather derived from the problem domain. Metadata just a means of retrieving this data, and many times, the only way.

Here is an example of implementing AssertType using C# and .net reflection. It is but one of the many ways that it can be done.

public void AssertType<Expected>(object actual)
{
    Assert.AreEqual(typeof(Expected).Name, actual.GetType().Name,
                     "type mismatch");
}

Work-Flow

Consider the following code:

class SalesOrder {
 public double CalculateCharges(TaxPolicy taxCalc) {
   double charge = // Some algorithm that totals up the sales
   return charge + taxCalc.getTax(charge); //Add the tax
 }
}

interface TaxPolicy {
 double taxCalc(double amount);
}        
 
TaxPolicy is an interface that we can create different implementations of for the various locales where we have to calculate tax.  We might have one for VAT (value added tax) for our European customers, another that calculated PST (provincial sales tax) for Canadian customers and so forth.  Each of these implementations would have its own test, of course.

But in testing SalesOrder, we would not want to use any of those “real” TaxPolicy implementations.  The reasons may be obvious, and we certainly cover this elsewhere, but just to summarize:
  1. We don’t want to couple the test of SalesOrder to any particular TaxPolicy.  If we just “picked one” then subsequently retire that object, we’ll break the test for SalesOrder even though SalesOrder was unchanged.
  2. We could see the test of SalesOrder fail when the object is operating correctly; the failure could be caused by a bug in the particular TaxPolicy implementation we chose.
  3. When that happens, we would have two tests failing for one reason (the test of SalesOrder and the test of the particular TaxPolicy implementation).  This is a bad road to be on.

The issue is easy to deal with... we could simply create an implementation of TaxPolicy that returns zero.  This would be a mock object[3], and would actually be a part of the test.

So, let’s say we did that.  One morning we start our day by running our tests.  They all are green.  We also happen to note our code coverage measurement and see that it is 100%.  Great!  100% of the code is covered, and all the tests are green!  
     
We have a strong sense confidence that nothing bad has been done to the code since we last encountered it.  Nobody has introduced a bug, and nobody has added code without an accompanying test.

Unfortunately, this confidence may be faulty.  Perhaps some other developer, innocently, made the following change.        

class SalesOrder {
 public double CalculateCharges(TaxPolicy taxCalc) {
   double charge = // Some algorithm that totals up the sales
   return charge;
 }
}

Bascially, SalesOrder does not call the getTax method on the TaxPolicy implementation at all.  This can happen for any number of reasons... perhaps the developer felt that the comment //Add the tax was not necessary, and so backspaced it out... and simply went too far.  Or, perhaps he inaccurately believed that the tax was being added elsewhere.  There are lots of reasons why mistakes like this are made.

Unfortunately, all the tests still pass. The real TaxPolicy implementations still work, and the SalesOrder not calling the mock produces the same additional tax change as calling it: zero.  Also, removing code will not lower the code coverage percentage.

What we’re missing, therefore, is a test that ensures that SalesOrder does, in fact, make use of the TaxPolicy object it is given.  This is a question of proper workflow between these objects, and it must have a test because it is required behavior.  

This is, in our experience, a very commonly-missed test.  This is not because the test is particularly difficult or laborious to write, it is because the developers simply don’t think of it.  

class MockTaxPolicy : TaxPolicy {
 bool gotCalled = false;
 public double getTax(double amount) {
   gotCalled = true;
   return 0.0;
 }

 public bool didGetCalled() {
   return gotCalled;
 }
}

…. and then we would add a new test:

public void testSalesOrderToTaxPolicyWorkflow() {
 MockTaxPolicy mock = new MockTaxPolicy();
 SalesOrder order = new SalesOrder();

 order.CalculateCharges(TaxPolicy mock);

 Assert.IsTrue(mock.didGetCalled());
}

This works because the test holds the mock by its concrete type (making didGetCalled() available to it) while SalesOrder holds it in an implicit upcast to TaxPolicy.  This encapsulates the “for testing only” method from the production code.

You should take care not to overdo this.  Simply because you can do something does not necessarily mean you should.  If a given workflow is already covered by a test of behavior, than you don’t want to test it again on its own.  We only want to create workflow tests where breaking the workflow per-se does not cause any existing test to fail.

Another way to think if it is this:  some workflows are specified, some are simply an implementation choice that might be changed in refactoring (without, in other words, changing the resulting behavior).  Only the specified (must be done) workflows should have tests written specifically for them.  Creating tests of workflows that are only implementation details will couple your test suite too tightly to your current implementation and will impede your ability to refactor.

Conclusions

The purpose in creating categories of tests was fourfold:
  1. Ensure that we remember to include all the types of tests needed to form a complete specification, including those that are often missed (constant and workflow specification being very typical)
  2. Capture the best practices for each category
  3. Promote consistency across the team/organization by creating a shared set of test categories that all developers know to create
  4. Add to the language of testing, improving communication

The list we presented here may not be comprehensive, but it serves as a good starting point.  Once we have the notion of these categories in place, we can enhance them and add new ones as we discover more ways to fully specify a system using tests.

-----

[1] In point of fact, we don’t actually completely agree with this method of categorizing the Design Patterns, but it does serve as a reasonable example of categorization in general.
[2] There are other ways to do this.  In another blog we will discuss the use of an “Any” class to make these “I don’t care” values even more obvious.
[3] We have lots to say about mocking.  For now, we’re keeping it simple.

[4] This is the Singleton pattern.  If you are unfamiliar with it:  
     http://www.netobjectives.com/PatternRepository/index.php?title=TheSingletonPattern


18 comments:

  1. Hi, guys!

    I remember a podcast for part 1 mentioning briefly constant specification, however, they were covered in greater detail only in Amir's answer to one of my questions. For me, there are some other things missing, like how to specify constant name and scope when we don't care about the value? I hope that either a future post or your book will ultimately cover this. By the way, is there any anticipated release date for the book?

    Another thing about the content of this podcast. As I understand, we're supposed to use construction specification only in cases of factories and when actual behavior takes place in constructors. So, in case where I have two constructors - one for dependency injection, like:

    public MessageProcessor(IReceiveChannel receiveChannel, IEncryptor encryptor, IDestinationChannel destination)
    {
    //just an assignment to private fields
    }

    and a second, parameterless constructor:
    public MessageProcessor()
    : this(new Socket(), new DesEncryptor(), new Socket())
    {}

    I wouldn't need to specify these two explicitly. Is my understanding correct?

    ReplyDelete
    Replies
    1. I just finished listening to the first part again and noticed that it actually covers how to specify type and scope of enumerated constants, so please disregard that part of the question.

      Delete
    2. No date as yet, we're working it out with out publisher.

      Construction is construction and as such needs to be tested.
      In the case of:
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket())
      we need a test that will fail in the case that one of the elements constructed is of the wrong type.
      This test is incredibly difficult to specify without looking at the internal implementation of the MessageProcessor.

      Naturally, this suggests that the design of the system is at fault -- we are violating the "don't cook and eat in the same place" rule, the separation of use from construction.

      This means that in new development you should never resort to such construct. At the least you should delegate the individual construction to protected methods that you can override to mock and call through a subclass test.

      Remember, the construction in and of itself is useless. This holds true to both constructors. If you pass an encryptor or socket you need to show that they are used at the right time.

      Along the lines of:
      Given an encryptor E
      When I create a messageProcessor mp
      Then calling mp.Process will result in E being called with the received data and the result of Process send to the destination.

      The only scenario I can think of where you would use the default constructor pattern is when you have just refactored legacy code, and used constructor chaining to enable dependency injection. This does not allow you to test what the default values are, but allows you to test that the MessageProcessor uses them correctly.

      Delete
    3. A question regarding the snippet you posted -- why are you prefixing the interfaces with 'I' (as in IEncryptor) -- why not call the type Encryptor?
      Prefixing interfaces with 'I' is a misinterpretation of the concept of Hungarian Notation. The idea is to prefix the name of a VARIABLE with its type. IEncryptor *IS* a TYPE, and you do not need to prefix types with their types because they have no special type in and of themselves - they are types.

      Hence, if you want to practice Hungarian Notation you could write:
      Encryptor encryptorDES = getDesEncryptor();

      Not:
      IEncrytpor encryptor = getDesEncryptor();

      Delete
    4. Hi, Amir, I agree, that this ISomething is not the most lucky choice. I used it out of habit, since I work in .NET related code and that's the standard we have. This is not the only thing I dislike in .Net conventions. Thanks for pointing this out, in future discussions with my peers, I'll point them right to this answer :-).

      I used the so called "constructor injection" (as named in Roy Osherove's book) because it's the simplest thing that works and, while introducing less sustainability (and I agree with you on the usefulness of "encapsulate construction" principle), it's the easiest to start with (and that's what we do in our team currently, because people are starting to write unit tests and some are very resistant to TDD and I don't want to discourage them). However, You make a VERY good point that the safety feeling coming in "I'm just using this constructor in parameterless constructor just above and I KNOW I'm not passing nulls" is a false one. In the future, I WILL want to extract the creation at least as soon as I start feeling pain of not having done this before. So then, I will have to put parameter validations in my constructor because the construction is encapsulated elsewhere and so my trust is now gone.

      Thanks for your invaluable insights and let me know what you think.

      Delete
    5. By the way, some further thoughts on the above.

      On the ISomething convention - I thought about what it could come from and I remember that not all think as Encryptor e; as an instance of a type. Some think about about such interface variables as empty slots for roles to be filled with collaborators, so seeing ISomething x; immediately points one to thinking "this is an empty slot for a collaborator and I can fill it with whatever I like", putting an emphasis that this field being here results from design needs, not from implementation ones (like library objects such as Random() that just serve as implementation mechanisms). So the reason may be convenience on one hand, on the other, this may be a way for someone to draw a line between a specification and implementation perspective.

      If I were to defend this convention, that's probably the argument I'd raise. What do you think?

      Delete
    6. One more thought - you said that a construct such as:
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket()) is incredibly difficult to specify, even given that other constructor called by this one is public. So, putting sustainability aside for a second and looking only from the perspective of testability, what's the difference between:

      class MessageProcessor
      {
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket()) {...}
      }

      and:

      class MessageProcessorFactory
      {
      public MessageProcessor Create()
      {
      return new MessageProcessor(new Socket(), new DesEncryptor(), new Socket());
      }
      }
      ?

      Delete
    7. Oh, and there is another statement from your answer I'd like to challenge: "IEncryptor *IS* a TYPE, and you do not need to prefix types with their types because they have no special type in and of themselves - they are types.". I don't know what you mean by special, however, from my point of view, treating classes and interfaces as types is a C language legacy, where the type was used only for compiler checks and the real behavior belonged to the instances of those types. C++ already makes this half-false by allowing classes to behave (so class is already not only a type, but a kind of entity), C# and Java going further and languages such as ruby making it very explicit that class is just another object that behaves as any other (in fact, you can type c = Class.new at runtime, thus generating a new class).

      Delete
    8. astral: Mar 5, 2012 12:25 AM said:

      >>One more thought - you said that a construct such as:
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket())
      is incredibly difficult to specify, even given that other constructor called by this one is public. So, putting sustainability aside for a second and looking only from the perspective of testability, what's the difference between:

      class MessageProcessor
      {
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket()) {...}
      }

      and:

      class MessageProcessorFactory
      {
      public MessageProcessor Create()
      {
      return new MessageProcessor(new Socket(), new DesEncryptor(), new Socket());
      }
      } <<

      1. I said the TEST for such construction is extremely difficult to specify.

      2. The difference is between:

      class MessageProcessor
      {
      public MessageProcessor(): this(new Socket(), new DesEncryptor(), new Socket()) {...}
      }
      AND
      class MessageProcessor
      {
      public MessageProcessor()
      //state variables here
      {
      source = GetSource();
      destination = GetDestination();
      encryptor = GetEncryptor();
      }
      protected virtual Encryptor GetEncryptor()
      { return defaultEncryptor; }
      }

      In the test:
      public class MessageProcessorTest
      {
      static Encryptor mockEncryptor;

      public void TestSomeMessageProcessorBehavior()
      {
      MockMessageProcessor testedMessageProcessor = new TestMessageProcessor();
      //
      // Test the behavior
      //
      }

      private class TestMessageProcessor: MessageProcessor
      {
      protected Override Encryptor GetEncrytpor
      {
      return mockEncryptor;
      }
      }
      }

      To test that the correct encryptor is used:

      public class MessageProcessorTest: MessageProcessor
      {
      public void TestFooEncryptorUsedByMessageProcessor()
      {
      Encryptor encryptor = GetEncryptor();
      Assert.AreEqual(typeof(FooEncryptor), encryptor.GetType());
      }
      }

      Delete
    9. astral: Mar 5, 2012 01:48 PM said:
      >> Oh, and there is another statement from your answer I'd like to challenge: "IEncryptor *IS* a TYPE, and you do not need to prefix types with their types because they have no special type in and of themselves - they are types.". I don't know what you mean by special,<<

      [AK] My bad, I meant SPECIFIC, not SPECIAL.

      >> however, from my point of view, treating classes and interfaces as types is a C language legacy, where the type was used only for compiler checks and the real behavior belonged to the instances of those types. C++ already makes this half-false by allowing classes to behave (so class is already not only a type, but a kind of entity), C# and Java going further and languages such as ruby making it very explicit that class is just another object that behaves as any other (in fact, you can type c = Class.new at runtime, thus generating a new class). <<

      OK, this goes into quicksand territory, but bear with me:
      When we say "Type" with mean the conceptual type. This could be an abstract class, and interface or a concrete class, which may or may not implement an interface. We are referring to the NAME used.

      The name is the name of the equivalent concept or entity in the domain problem.

      Animal, for example, could be an interface refers to the concept of an animal.
      Animal as a concept could have a concrete instance called Dog. We say: "Dog is an Animal"(1). The actual implementation of Dog is, at this point, not important. So, although Dog is more specific than Animal it is still conceptual.
      The class Animal may have attributes that are common to all animals, such as "NAME". This is still a conceptual property of an Animal. We do not know how it will be implemented. Again, note the constant use of the word 'conceptual'.

      I could write in my code:
      Animal dog = Dog.GetInstance();
      I could also say:
      Dog dog = Dog.GetInstance();

      Now, can you tell me at this point, if Dog is a concrete class or an abstract (conceptual) class?

      Do you care?

      Delete
    10. By the way, when I read your version of MessageProcessor example, I still have some doubts as to where to stop specifying construction.

      Let's take for example the following test from above example:

      >>To test that the correct encryptor is used:

      public class MessageProcessorTest: MessageProcessor
      {
      public void TestFooEncryptorUsedByMessageProcessor()
      {
      Encryptor encryptor = GetEncryptor();
      Assert.AreEqual(typeof(FooEncryptor), encryptor.GetType());
      }
      }<<

      Here, you still don't know whether the _right_ encryptor is used. You only know that it's the encryptor of correct type. So what if DES encryptor for message processing has to be created with some special values? You could, for sure, include the encryptor's default values in the test. But what if the encryptor needs to be initialized with some other object? Would you include THAT object in the test as well? And what if this "other" object needs to be created with some other object with some certain values etc. etc.

      So, you have the following:
      correctly initialized Encryptor needs specifically initialized other object which needs specifically initialized another object.

      I could, for sure, cut that by putting a "other object" factory in the Encryptor and mocking it in the test, but that leaves me with the question: how do I specify that correct factory is used? I could use another factory for creating encryptor with correct factory, but how do I specify that this factory is used for creating encryptors with correct factory and not another factory?... And so on and on and on... and I feel that I will always end up with a creation that I cannot specify.

      Thinking about it makes my head spin. Do you ever run into such issues in practice or is it plainly theorethical or is my thinking broken somewhere?

      Delete
  2. astral: Mar 4, 2012 11:43 PM said:
    >> (2)In the future, I WILL want to extract the creation at least as soon as I start feeling pain of not having done this before. So then, (1)I will have to put parameter validations in my constructor because the construction is encapsulated elsewhere and so my trust is now gone. <<

    (1) If you use a factory, or a factory method in the constructor, as in:

    class MessageProcessor() {
    Encryptor encryptor;
    public MessageProcessor() {
    encryptor = EncryptorFactory.GetInstance(); // External Factory Object
    //or
    encyrptor = GetEncryptorInstance(); // Internal Overridable Factroy Method
    }
    }

    You do *NOT* have to do parameter validation in the case as it is not the responsibility of the constructor to figure out what to use, it is the responsibility of the factory. It *is* the responsibility of MessageProcessor class to USE the encryptor correctly.

    (2) Since it was the responsibility of the factory to create the correct object and initialize it correctly we need to test THAT.
    Using a default constructor you will confront the pain of using it IMMEDIATELY. The alternative, which is what you guys are probably doing now, is NOT to test this and this is a slippery slope...

    ReplyDelete
  3. astral: Mar 5, 2012 12:01 AM said:

    >> On the ISomething convention - I thought about what it could come from and I remember that not all think as Encryptor e; as an instance of a type. Some think about about such interface variables as empty slots for roles to be filled with collaborators, so seeing ISomething x; immediately points one to thinking "this is an empty slot for a collaborator and I can fill it with whatever I like", putting an emphasis that this field being here results from design needs, not from implementation ones (like library objects such as Random() that just serve as implementation mechanisms). So the reason may be convenience on one hand, on the other, this may be a way for someone to draw a line between a specification and implementation perspective. <<

    I have to admit that I do not understand your concern is.

    1. I will never write
    Encryptor e;
    I will write
    Encryptor encryptor;

    Now, from the perspective of the consumer of the encryptor, the MessageProcessor, does it care if Encryptor is a concrete type of an abstract type? The answer is NO. the only thing that MessageProcessor (and the person who wrote it) cares for is that the Encryptor encrypts.

    I do not understand the whole collaborator discussion, so forgive me for perhaps saying something that is obvious.

    Given that the test must be written first, and therefore the API must be specified prior to the implementation, how can the interface contain an element that is implementation dependent?

    ReplyDelete
  4. A lot of your comments, Astral, refer to technical implementation issues. Remember, that when you do TDD, there is NO implementation. The test goes first....

    ReplyDelete
    Replies
    1. This is true - I think this may be the source of my doubts. After reading your comments, I understand that most of my arguments refer to the situation where only part of the team is using TDD, other part is only refactoring for testability and another part just doesn't care and writes redundant unit tests. So, for example, naming something ISomething helped me recognize class testability before adding further tests to suite created by someone else (because abstract classes, when written without testability in mind - e.g. doing complex stuff in constructor or initializing its fields inline with concrete instances - are difficult to mock), however, when doing proper TDD, I imagine this is not really an issue.

      Also, thanks for the tip on parameter validation being actually a different responsibility. Now, I'm beginning to truly appreciate what construction encapsulation achieves.

      Thanks for bearing with me, I tried to push on the creation question because I knew it requires some mind shift and it was very hard for me to do this alone (and that's why I really enjoy reading this blog).

      By the way, as for collaborators stuff, I was referring to the explanation from: www.jmock.org/oopsla2004.pdf (yet I have to admitt that the reference was not literal, because those guys use the collaborator-slot metaphor for different things)

      Delete
  5. One more question - when specifying a new class, do write construction specification as a first test, or as one of the last ones? For simple specifications like "When I ask X to create an object, it should be a Dog", it's no problem to write it in the beginning of class creation. However, when taking on a test such as TestFooEncryptorUsedByMessageProcessor mentioned above (is it still a creation test?), I have to know a little bit more about what other objects do objects of this class need to perform the expected behaviors. And this is something I usually know when I start analysis through functional and workflow tests. That's why I'm a little bit confused.

    By the way, I may be alone in this need and it may not align strictly with the goal of the book (which is showing how the code and tests evolves over time), but I would make a great use of some sample app code shipping with the book to demonstrate how all these ideas work in practice together. And if this source code was annotated with pointers to the chapters for rationale, it would be a great help for us rookies to learn.

    ReplyDelete
  6. Personally, I think it's a good idea to write tests that are about the use of the constructor as early as possible, because these tests may call into question exactly how the constructor is specified, and if you decide to change it, this can cause maintenance across many tests (because constructors cannot be overridden). So, I like to get these tests accomplished when the suite is relatively small.

    ReplyDelete
  7. I am wondering how you go about testing the creation of the Decorator pattern. When I get the object back from the factory, I only know what the first item in the list is, unless I get access to the internals of decorator object. Is it appropriate in this scenario to expose this just for the purpose of testing?

    ReplyDelete