Tuesday, 7 May 2013

When a 'test' becomes something else

(this post is adapted from my work-in-progress open source TDD tutorial)

Everyday life example

I studied in Łódź, a large city in the center of Poland. As probably all other students in all other countries, we've had lectures, exercises and exams. The exams were pretty hard, especially considered that my computer science group was on the faculty of electronic and electric engineering, so we had to grasp a lot of classes that didn't have anything to do with programming, for instance electrotechnics, solid-state physics or electronic and electrical metrology.

Knowing that the exams were difficult and that it was hard to learn everything during preparation, the lecturers would give us exemplary exams from previous years. The questions were different than during actual exams, but the structure and types of questions asked (practice vs. theory etc.) was similar. We would usually get these exemplary questions before we started learning really hard (which was usually at the end of semester). Guess what happened then? As you might suspect, we did not use the tests we received just to 'verify' or 'check' our knowledge after we finished learning. Those tests were actually the first thing we went to before even starting to learn. Why was that so? What use were the tests when we knew we wouldn't know most of the answers?

I guess my lecturers would disagree with me, but I find it quite amusing that what we were really doing back then was 'Lean'. Lean is an approach where, among others, there's a rigorous emphasis on eliminating waste. Every feature or product that's produced while not being needed by anyone is considered waste. That's because if something's not needed, there's no reason to assume it will ever be needed. In such case the entire feature or product is a waste - it has no value. Even if it WILL be ever needed, it will require some rework anyway to fit the real customer needs. In this case, the initial amount of work that went into the parts of solution that had to be replaced by another parts anyway is a waste - it never brought any money (I'm not talking about such things as customer demos - their value lies somewhere else).

So, in order to eliminate waste, there's huge pressure nowadays to "pull features from demand" instead of "pushing them" into the product "for future". In other words, every feature is there to satisfy concrete need. If not, the effort is considered wasted and the money drown.

Going back to the exams, why the approach of first looking through the exemplary tests can be considered 'lean'? That's because, when we treat passing an exam as our goal, then everything that does not put us closer to this goal is considered a waste. Let's suppose the exam is theory only - why then practice the exercises? It would probably pay off a lot more to study theoretical side of the topics. Such knowledge could be obtained from those exemplary tests. So, the tests were a kind of specification of what was needed to pass the exam, letting us pull the value (i.e. our knowledge) from demand (information obtained from a realistic tests) rather that pushing it from implementation (i.e. learning everything in a course book chapter after chapter).

So the tests became something else. They proved very valuable before the 'implementation' (i.e. learning for the exam) because:

  1. they helped us focus on what was needed to reach our goal
  2. they brought our attention away from what was not needed to reach our goal

That was the value of a test before learning. Note that the tests we would usually receive were not exactly what we'd encounter at the time of exam, so we still had to guess. Still, the role of a test as specification of a need was already visible.

Taking It To The Software Development Land

I chose this lengthy metaphor to show you that 'test' is really another way of specifying a requirement or a need and that it's not something that's counter-intuitive - it occurs in our everyday lives. This is also true in software development. Let's take the following 'test' and see what kind of needs it specifies:

var reporting = new ReportingFeature();
var anyPowerUser = Any.Of(Users.Admin, Users.Auditor);
Assert.True(reporting.CanBePerformedBy(anyPowerUser));

(In this example, we used Any.Of() method that returns any enumeration value from the specified list. Here, we say "give me a value that's either Users.Admin or Users.Auditor".)

Let's look at those (only!) three lines of code, imagining that the production code that makes this 'test' pass does not exist yet. What can we learn from these three lines about what the code needs to supply? Count with me:

  1. We need a reporting feature
  2. We need to support a notion of users and privileges
  3. We need to support a domain concept of power user, who is either an administrator or an auditor
  4. Power users will need to be privileged to use the reporting feature (note that it does not specify which other users should or should not be able to use this feature - we'd need a separate 'test' for that).

Also, we are already after the phase of designing an API that will fulfill the need. Don't you think it's pretty much information about the application from just three lines of code?

A Specification Instead of a Test Suite

I hope that you can see now that what we called 'a test' is really a kind of specification. This "discovery" is quite recent, so there isn't a clear terminology on it yet. Some like to call the process of using tests as specifications: Specification By Example, to say that the tests are really examples that help specify and clarify the behavior of developed part of functionality. The terminology is still not rock-solid, so you might encounter different naming for different things. For example, a 'test' is often referred to as 'spec', or an 'example', or a 'behavior description', or a 'specification statement' or a 'fact about the developed system' (the xUnit.NET framework marks each 'test' with a [Fact] attribute, suggesting that by writing it, we're stating a single fact about developed code. By the way, xUnit.NET also allows us to state 'theories' about our code, but let's leave it for now).

From your experience, you may know paper or word specifications, written in plain English or other spoken language. Our specification is different than these specifications in at least few ways:

  1. it's written one statement at a time.
  2. it's executable - you can run it to see whether the code adheres to the specification or not.
  3. it's written in source code rather than in spoken language - which is both good (less room for misunderstanding - source code is the most formal and structured way of writing specifications) and bad (great care must be taken to keep such specification readable).

Friday, 12 April 2013

Motivation - the first step to learning TDD

So, you want to learn TDD, huh? First off, let me ask a question 'why?'.

I'll tell you a story - few years ago, I had an apprentice, who wanted to learn TDD as well. We started working on a small project together for him to grasp the necessary skills through practice, with me sitting next to him, providing guidance. He showed up three, four times, then he resigned, having 'more urgent things' and 'no time'. Since then, he did not progress in TDD at all. Did he really want to learn?

See, sometimes people don't really want to learn something or don't want so much as to dedicate the necessary time and resources to this. Sometimes they just think they need to do it for whatever reasons - getting promotion at work, gaining a certificate, add something to CV or just 'stay up to date' with recent hypes, one of them being Test-Driven Development.

Others want to learn TDD for reasons they themselves made up. Knowing that TDD is valued and praised by others, they draw conclusions that it has to be good. Why is that so? Err... maybe... "the code will be more tested"? Or... ekhem... "the network of tests will be tighter"? And why do Test-First? Well... hmm... "to ensure tests are written for everything"? While these statements might be partially true, they don't even touch the essence of the "why" of TDD, and quickly turn into something like: "I don't really need TDD, because I need tests that give me confidence on broader scope"(*) or: "Why do I need unit tests when I already have integration tests, smoke tests, sanity tests, exploration tests...?". Then TDD gets abandoned, before actually ever being achieved in the first place.

You need to be highly motivated on this one. If you're not, hey, I heard the new series of Game Of Thrones is on the TV, why don't you check it out instead? Ok, I'm just teasing, however, as some say, TDD is "easy to learn, hard to master" (don't actually know who said it first, I searched the web and found it in few places where none of the writers gave credit to anyone else for it, so I decided just to mention that I'm not the one that coined this description) , so without some guts to move on, it will be hard.

How TDD feels like

Me and my brother liked to play video games in our childhood - one of the most memorable being Tekken 3 - a japanese tournament beat'em up for Sony Playstation. To finish the game with all warriors, unlock all hidden characters, mini-games etc. took about a day. Some could say the game had nothing to offer since then. Why then did we spend over a year on it?

It's because each fighter in the game had a lot of combos, kicks and punches that could be mixed in a variety of ways. Some were only usable in some situations, others were something you could throw at your opponent almost anytime without a big risk of being exposed to counterattacks. You could side-step to evade enemy's attacks and, most of all, you could kick another fighter up in the air where they could not block your attacks and you were able to land some nice attacks on them before they fell down. These in-the-air techniques were called "juggles". There were magazines that published a list of new possible juggles each month and the hype stayed in the gaming community for well over a year.

Yes, Tekken was easy to learn - you could put one hour into training the core moves of a character and then be able to "use them", but what made you a great fighter was the experience and knowledge on which techniques were risky or not, which ones could be used in which situations, which ones, if used one after another, gave the opponent little chance to counterattack etc. No wonder that soon, many tournaments sprang, where players could clash for glory, fame and rewards. Even today, you can watch some of the matches on youtube.

TDD is like Tekken. You've probably heard the mantra "red-green-refactor" or the general advice "write your test first, then the code", maybe you even did some experiments on your own where you were trying to implement a bubble-sort algorithm or other simple stuff by starting with a test. But that's all like practicing Tekken by trying out each move on its own on dummy opponent, without the context of real-world issues that make the fight really challenging. And while I think such exercises are (somewhat) useful, you need to understand the bigger picture.

Some people I talk with about TDD sum up what I say to them as: "That's really demotivating - there are so many things I have to watch out for that it makes me never want to start!". Easy, don't panic - remember the first time you tried to ride a bike - you must've been really far back then from knowing traffic regulations and following road signs, but that didn't really keep you away, did it? I myself found TDD very exciting. Some guys my age already think they know all about coding, are bored with it and can't wait until they move to management or requirements or business analysis, but hey! I have a new technique that makes my coding career challenging again! And it's not something I can use only when I pay to Microsoft or Oracle or anybody else - it's quite portable, making me a better developer overall!

(*)To be more precise, TDD is not only about unit tests, but is viewed as such by many.

Wednesday, 27 March 2013

Implementation Readability vs Design Readability and what TDD has to do with it?

Many thanks to Łukasz Bigos for discussion and inspiration.

Recently I had a discussion with few of my colleagues on readability of Test-driven (or more precisely - loosely coupled) code. The thing that surprised me the most was the argument that loosely coupled code is unreadable. The surprise comes from my deep conviction that TDD help achieve extremely readable code.

In my mind, TDD leads to well factored, small classes with single responsibilities, but in some others' minds, it's just "obfuscating the picture" and they "don't know what's happening in this code because everything is hidden and split across many classes and methods".

So I thought about it some more, talked a little more and finally I reached a conclusion on why there's a disagreement between us.

Two ways of understanding readability

It appears that, depending on our habits, "readable" means something different to us. There are two kinds of readability I learned to differentiate: implementation readability and design readability. Let's try to look at what each one is about and who & why finds this particular kind of readability relevant.

1. Implementation readability

Let's imagine we're developing client-server application for project management. The part we'll be dealing with is the server-side processing of a request to add new issue to existing project. By looking at the example code, we'll see how and why implementation readability plays out in practice. Without further ado, ladies and gentlemen, the code:

public void AddNewIssueToProject(Request request, Response response)
{
  try
  {
    if(!request.ContainsKey("senderIp") 
       || string.IsNullOrWhiteSpace(request["senderIp"] as string))
    {
      response.Write("Wrong sender IP");
      return;
    }

    if(!request.ContainsKey("projectId") 
       ||  string.IsNullOrWhiteSpace(request["projectId"] as string))
    {
      response.Write("Invalid project ID");
      return;
    }

    if(!request.ContainsKey("issueData") || 
      request["issueData"] as Dictionary<string, string> == null)
    {
      response.Write("issue data not passed");
      return;
    }

    var issueData 
      = request["issueData"] as Dictionary<string, string>;

    if(!issueData.ContainsKey("Title") || 
        string.IsNullOrWhiteSpace(issueData["Title"]))
    {
      response.Write("No title for issue supplied. " +
        "Issue title must be at least one readable character");
      return;
    }

    if(!issueData.ContainsKey("Content") || 
        string.IsNullOrWhiteSpace(issueData["Content"]))
    {
      response.Write("No content for issue supplied. " +
        "By our policy, every issue must be described with details");
      return;
    }

    if(!issueData.ContainsKey("Severity") 
       || string.IsNullOrWhiteSpace(issueData["Severity"]))
    {
      response.Write("No severity supplied, " + 
        "although the form forces this." +
        " Are you trying to send us raw HTTP request?");
      return;
    }

    var projectId = request["projectId"] as string;
    var issueTitle = issueData["Title"];
    var issueContent = issueData["Content"];
    var severity = issueData["Severity"];

    var queryString = string.Format(
      "INSERT INTO ProjectIssues VALUES ({0}, '{1}', '{2}', '{3}')",
      projectId, issueTitle, issueContent, severity);

    var query = new Query(queryString);
    using(var connection = DatabaseConnection.Open("dbName=Abcdef"))
    {
      query.ExecuteThrough(connection);
    }

    response.Write("Everything's OK!");
  }
  catch(Exception e)
  {
    response.Write("Unexpected error, see log for details");
    _log.Error(e);
  }
}

While this snippet may seem like a classic spaghetti code to those of you who are more design-infected, there is actually a significant benefit behind it. The benefit is that without looking anywhere else, we can deduce the state of the method variables in every line and what precisely will happen in the system when we run this code. Because the method is made up mostly of primitive or library constructs, we're able to "debug" it with our eyes only, without jumping here and there to gather the parts of the functionality necessary to "understand" the code.

Wait, did I just write "understand"? What kind of "understanding" are we talking about here? Well let's take the following line as example:

    var projectId = request["projectId"] as string;

When reading this code, by the time we arrive at this line we already know that some values are put inside the dictionary and that they're non-null and not some other value like empty string. What are "dictionary", "null" and "empty string"? They're implementation details! Are they connected to the domain of request processing? No. Do they help describe the high-level work-flow? No. Do they help us understand the requirements? Not much. Can we read from the code what steps does this method consists of or where each step begins and where it ends? No, we can extract it somehow by comparing the code to our domain knowledge or experience (e.g. most of us know that each request coming to the server has to be validated), but again, this is something extracted from the code, not something that's already there.

So, the conclusion is that this style of writing is better at describing how the code works, while doing a poor job of describing what the code is for.

Benefits of implementation readability

So, who benefits from having a high implementation readability? Well, there are times when we're given some assignments in a code we don't know. Let's say that our boss told us to fix a bug in an application that should write string "abcd" to Windows registry, but it writes "abc". Now, we don't care about the big picture - all we care for is that we know what the single external behavior is and what it should be instead (we don't even care why), so we search for this one line in the code that is responsible for the behavior to replace it with something else. While searching, everything that is not the sought construct (including design and domain knowledge), is in our way. From this point of view, the ideal situation would be to have the whole application in a single source file so that we can use our text editor to search it for the word "Registry", then examine each occurrence and make the fix. In other words, we're acting as a little smarter "search and replace" mechanisms. The single-responsibility classes just get in our way and make us "pointlessly" navigate through a web of objects we neither want nor need to understand (some would say that we're on level 1 or 2 of Dreyfus model of skill acquisition).

While cases such as this one happen, we must realize that they're not the cases we're trying to optimize for. In any but the simplest cases, making a novice go into a piece of code to make changes without understanding of domain or design will lead to degradation of the code and probably few bugs (when someone is not aware of the big picture, they might fix a bug in a way that introduces another bug somewhere else, because of another scenario they didn't have any idea of). So the case we want to optimize for is someone going into the code with a need to understand the domain and the design first before making a change. It's best if they can derive at least part of this knowledge from the code itself and easily map parts of domain knowledge to places in the code. And this is how we arrive at...

2. Design readability

Part of good object-oriented design is implementation hiding, i.e. we want to hide how a particular piece of functionality is implemented so that we can change it later. Also, when we're equipped with domain and design knowledge, we want this knowledge stand out, not to be obfuscated by implementation details. To give you a quick example of what I mean: when talking about web server session storage, we say that "a user is assigned a persistent session", not that "a database holds a serialized hashtable indexed by user ID". Thus, we want to see the first clearly visible in the code, not the latter. Otherwise, readability is hurt.

Let's now take a little piece of refactored request handling code that we began with and see whether we can see any improvements:

class AddingNewIssueToProject : Scenario
{
    UserInteraction _user;
    AddIssueToProjectRequest _addIssueToProjectRequest;
    PersistentStorage _issueStorage;

    public AddingNewIssueToProject (
      AddIssueToProjectRequest requestForAddingIssueToProject, 
      UserInteraction userInteraction,
      PersistentStorage issueStorage)
    {
      this._requestForAddingIssueToProject 
        = requestForAddingIssueToProject;
      this._user = userInteraction;
      this._issueStorage = issueStorage;
    }

    public void GoThrough()
    {
      try
      {
        _requestForAddingIssueToProject.Validate();
        Issue issue = _requestForAddingIssueToProject.ExtractIssue();
        issue.StoreInside(_issueStorage);
        issue.ReportSuccessfulAdditionTo(_user);
      }
      catch(ScenarioFailedException scenarioFailed)
      {
        _user.NotifyThat(scenarioFailed);
      }
   }
}

As you can see, there's almost no "implementation" here. No strings, no nulls, no dictionaries, lists, data structures, no addition, subtraction, multiplication etc. So how can it be readable as it tells very little of the external application behavior? Count with me:

  1. The class name is AddingNewIssueToProject, which says that this class is all about this single scenario (in other words, when something is wrong with different scenario, this is not the place to look for root cause). Moreover, it implements a Scenario interface, which gives us a clue that each scenario in the application is modeled as object and we can most probably find all scenarios supported by the application by searching for classes implementing Scenario interface.
  2. It depends on three interfaces: UserInteraction, AddIssueToProjectRequest and PersistentStorage, which suggest that the scope of the scenario is: communication with user, working with the user-entered data and saving data to persistent storage.
  3. The GoThrough() method contains the sequence of steps. We can clearly see which steps the scenario consists of and what is their order, e.g. looking at this method, we are sure that we're not trying to save invalid Issue object, since storing issue is after validating correctness.
  4. Looking at the invocation of ExtractIssue() method, we can see that the new issue that's being saved is made of data entered by user.
  5. The object _issueStorage is of type PersistentStorage, suggesting that, after the issue is saved, we'll be able to retrieve it later, most probably even after the server restart.
  6. Each of the steps of the scenario is allowed to fail (by throwing ScenarioFailedException) and the user is immediately notified of the failure.
  7. If the adding issue scenario ever needs to perform any additional step in the future, this is the place where we'd start adding this change.

Not bad for just few lines of code that contain practically no implementation details, huh?

Which one to prefer?

Personally, I strongly prefer design readability, i.e. I want to draw as much domain knowledge and design constraints knowledge from the code (not to be mistaken with overdesign) as possible. One reason for this is that design readability makes it much easier to introduce changes that are aligned with the "spirit" of the code that's already in place and such changes are usually much more safe than hacking around. Another reason is that implementation readability is impossible to maintain over the long haul. Let's take the following snippet from our example:

if(!issueData.ContainsKey("Severity") 
   || string.IsNullOrWhiteSpace(issueData["Severity"]))
{
  response.Write("No severity supplied, " + 
    "although the form forces this." +
    " Are you trying to send us raw HTTP request?");
  return;
}

This is a part of code responsible for adding new issue, but we can imagine it making its way to scenario for editing existing issue as well. When this happens, we have two choices:

  1. Copy-paste the code into the second method, creating duplication and redundancy (and leading to situations where one change needs to be made in more than one place, which, according to Shalloway's Law is asking for trouble)
  2. Extracting the code at least to a separate method that already defeats implementation readability partially, because each time you need to understand what piece of code does, you need to understand an additional method. All you can do is to give this method an intent-revealing name. Anyway, introducing new methods just to remove redundancy leaves us with with code where sections of implementation details are mixed with sections of domain work-flows stated in terms of calling domain-specific methods on domain-specific objects. From such code, we can easily deduce neither full work-flows, nor all implementation details. Such code is better known under the name of Spaghetti Code :-),

An object oriented system is not a set of streams of primitive instructions. It is a web of collaborating objects. The more complex the system, the more this metaphor becomes visible. That's why, instead of trying to put all implementation in one place to make it "debuggable", it's better to create a cohesive and readable description of that collaboration. A description that reveals domain rules, domain workflows and design constraints.

Of course, the choice is yours. Until you decide to do TDD, that is. That's because TDD strongly encourages clean design and separation of domain and design from implementation details. If good design guidelines are not followed, tests get longer and longer, slower and slower, more and more test logic is repeated throughout the tests etc. If that's where you are now, better revise your approach to design!

What do you think?

I'm dying to know what your opinions and observations on these topics are. Do you find the distinction between implementation and design readability useful? Which one you prefer? Please let me know in the comments!

Monday, 4 March 2013

The Two Main Techniques in Test-Driven development, part 2

Today, I'd like to continue where I left last time. As you might remember, the first post was about Need-Driven Development and this one's gonna be about a technique that's actually older and it's called Triangulation.

Triangulation

History

The first occurence of the term triangulation I know about is in Kent Beck's book Test Driven Development: By Example where Kent describes it as the most conservative technique of test-driving the implementation. It's one of the three core techniques of classic TDD.

Description

The three approaches to test-driving the implementation and design described in Kent's book are:

  1. Write the obvious implementation
  2. Fake it ('till you make it)
  3. Triangulate

Kent describes triangulation as the most conservative technique, because following it involves the tiniest possible steps to arrive at the right solution. The technique is called triangulation by analogy to radar triangulation where outputs from at least two radars must by used to determine the position of a unit. Also, in radar triangulation, the position is measured indirectly, by combining the following data: range (not position!) measurement done by the radar and the radar's own position (which we know, because we put the radar there).

These two characteristics: indirect measurement and using at least two sources of information are at the core of TDD triangulation. Basically, it says:

  1. Indirect measurement: Derive the design from few known examples of its desired external behavior by looking at what varies in these examples and making this variability into something more general
  2. Using at least two sources of information: start with the simplest possible implementation and make it more general only when you have two or more examples

Triangulation is so characteristic to the classic TDD that many novices mistakenly believe TDD is all about triangulation.

Example

Suppose we want to write a method that creates an aggregate sum of the list. Let's assume that we have no idea how to design the internals of our custom list class so that it fulfills its responsibility. Thus, we start with the simplest example:

[Test]
public void 
ShouldReturnTheSameElementAsASumOfSingleElement()
{
  //GIVEN
  var singleElement = Any.Integer();
  var listWithAggregateOperations 
    = new ListWithAggregateOperations(singleElement);

  //WHEN
  var result = listWithAggregateOperations.SumOfElements();

  //THEN
  Assert.AreEqual(singleElement, result);
}

The naive implementation can be as follows:

public class ListWithAggregateOperations
{
  int _element;

  public ListWithAggregateOperations(int element)
  {
    _element = element;
  }

  public int SumOfElements()
  {
    return _element;
  }
}

It's too early to generalize yet, as we don't have another example. Let's add it then. What would be the next more complex one? Two elements instead of one. As we can see, we're beginning to discover what's variable in our design. The second spec looks like this:

[Test]
public void 
ShouldReturnSumOfTwoElementsAsASumWhenTwoElementsAreSupplied()
{
  //GIVEN
  var firstElement = Any.Integer();
  var secondElement = Any.Integer();
  var listWithAggregateOperations 
    = new ListWithAggregateOperations(firstElement, secondElement);

  //WHEN
  var result = listWithAggregateOperations.SumOfElements();

  //THEN
  Assert.AreEqual(firstElement + secondElement, result);
}

And the naive implementation will look like this:

public class ListWithAggregateOperations
{
  int _element1 = 0;
  int _element2 = 0;

  public ListWithAggregateOperations(int element)
  {
    _element1 = element;
  }

  //added
  public ListWithAggregateOperations(int element1, int element2)
  {
    _element1 = element1;
    _element2 = element2;
  }

  public int SumOfElements()
  {
    return _element1 + _element2; //changed
  }
}

Now the variability in the design becomes obvious - it's the number of elements added! So now that we have two examples, we see that we have redundant constructors and redundant fields for each element in the list and if we added a third spec for three elements, we'd have to add another constructor, another field and another element of the sum computation. Time to generalize!

How do we encapsulate the variability of the element count so that we can get rid of this redundancy? A collection! How do we generalize the addition of multiple elements? A foreach loop through the collection!

First, let's start with writing a new, more general unit spec to showcase the new desired design (the existing two specs will remain in the code for now):

[Test]
public void 
ShouldReturnSumOfAllItsElementsWhenAskedForAggregateSum()
{
  //GIVEN
  var firstElement = Any.Integer();
  var secondElement = Any.Integer();
  var thirdElement = Any.Integer();
  var listWithAggregateOperations 
    = new ListWithAggregateOperations(new List<int>
      { firstElement, secondElement, thirdElement});

  //WHEN
  var result = listWithAggregateOperations.SumOfElements();

  //THEN
  Assert.AreEqual(firstElement + secondElement + thirdElement, result);
}

Note that we have introduced a constructor taking a list of arbitrary number of elements instead of just the values. Time to accommodate it in the design and bring the generalization we have just introduced in our spec into the implementation:

public class ListWithAggregateOperations
{
  List<int> _elements = new List<int>();

  public ListWithAggregateOperations(int element)
    : this(new List<int>() { element }) //changed
  {
  }

  public ListWithAggregateOperations(int element1, int element2)
    : this(new List<int>() { element1, element2 }) //changed
  {
  }

  //added
  public ListWithAggregateOperations(List<int> elements)
  {
    _elements = elements;
  }

  public int SumOfElements()
  {
    //changed
    int sum = 0;
    foreach(var element in _elements)
    {
      sum += element;
    }
    return sum;
  }
}

As we can see, the design is more general and I made the two existing constructors as a simple delegation to a new, more general one. Also, the first two specs ("one element" and "two elements") still pass with the new, more general implementation under the hood, meaning that we didn't break any existing behavior. Thus, it's now safe to remove those two specs, leaving only the most general one. Also, we can remove the redundant constructors, leaving the implementation like this:

public class ListWithAggregateOperations
{
  List<int> _elements = new List<int>();

  public ListWithAggregateOperations(List<int> elements)
  {
    _elements = elements;
  }

  public int SumOfElements()
  {
    int sum = 0;
    foreach(var element in _elements)
    {
      sum += element;
    }
    return sum;
  }
}

And voilà! We have arrived at the final, generic solution. Note that the steps we took were tiny - so you might get the impression that the effort was not worth it. Indeed, this example was only to show the mechanics of triangulation - in real life, if we encountered such simple situation we'd know straight away what the design would be and we'd start with the general specs straight away and just type in the obvious implementation. Triangulation shows its power in more complex problems with multiple design axes and where taking tiny steps helps avoid "analysis paralysis".

Principles

These are the main principles of Triangulation:
  1. Start writing specs from the simplest and most obvious case, increasing the complexity of the specs and the generality of implemntation as you add more specs.
  2. Generalize only when you have at least two examples that show you which axis of design change needs to be generalized.
  3. After arriving at the correct design, remove the redundant specs (remember, we want to have one spec failing for single reason)

Related Concepts

Inside-out development

Triangulation is a core part of inside-out TDD style, where one uses mocks sparingly and focuses on getting the lower-layer (i.e. the assumptions) right before developing a more concrete solution on top of it.

Can triangulation be used as a part of outside-in development (as described last time)? Of course, although it's probably used less often. Still, when you have a piece of functionality with well-defined inputs and outputs but don't know what the design behind it could be, you can go ahead and use triangulation whether you're developing outside-in or inside-out.

Acceptance tests/specifications

Even when doing Need-Driven Development using mocks etc., triangulation can be very useful at the acceptance level, where you can try to derive the internal design of whole module based on the tests that convey your understanding of domain rules.

Applicability

As I stated before, triangulation is most useful when you have no idea how the internal design of a piece of functionality will look like (e.g. even if there are work-flows, they cannot be easily derived from your knowledge of the domain) and it's not obvious along which axes your design must provide generality, but you are able to give some examples of the observable behavior of that functionality given certain inputs. These are usually situations where you need to slow down and take tiny steps that slowly bring you closer to the right design - and that's what triangulation is for!

Personally, I find triangulation useful when test-driving non-trivial data structures.

Thursday, 21 February 2013

The Two Main Techniques in Test-Driven development, part 1

Today, I'd like to write a bit about different techniques for test-driving development. There are actually two of them which I'd like to discuss: Need-Driven Development and Triangulation. Part one is about NDD, Triangulation shall be covered in part two.

Need-Driven Development

History

Need-Driven Development is a term that's less commonly used nowadays. It was coined in a paper Mock Roles Not Objects, but has since been somewhat abandoned. The authors of the mentioned paper wrote a book later on and the term Need-Driven Development is missing from it. Anyway, the approach itself has not changed and is by far one of the most popular.

Description

Imagine you're building a plane and the requirement for it is to transport a guy named John from Poland to US. Imagine you start with computer simulation (in a fake context) of putting a guy in the air - he would fall, the simulation says. Ok, so we need to put him on something - we introduce floor. But the floor would fall as well. So we need something to hold it in the air - we introduce engine. But we need to keep the balance in the air somehow - we introduce wings. And so on, and so on. This is the philosophy of NDD - start with the main thing you need without having anything and then figure out what else you need to do this and the figure out what this "else" needs...

NDD is an outside-in approach to development using unit tests and mock objects that stand for the context which does not yet exist. This way, we start with what our current concrete object needs and shape its context (which is filled in by mock objects) based on those needs. So in this approach, mocks are used as a tool for discovering interactions - we're free to shape the mocked interface anyway we like so that they're best suited to interact with the concrete object we're dealing with now.

As the fathers of NDD put it:

We use Mock Objects to let us write the code under test as if it had everything it needs from its environment. This process shows us what an object’s environment should be so we can then provide it.

Example

Let's take as an example the case from my previous post, which specified a simplified logic for logging in:

  1. When supplied credentials are valid, the Log-in process shall setup response as positive and redirect me to the home page
  2. When supplied credentials are invalid, the log-in process shall setup the response as negative and redirect me to the login page.
[Test]
public void 
ShouldSetResponseAsPositiveAndRedirectToHomePageWhenPerformedWithValidCredentials()
{
  //GIVEN
  var response = Substitute.For<Response>();
  var validCredentials = Substitute.For<UserCredentials>();
  var homePage = Substitute.For<Page>();
  var loginPage = Substitute.For<Page>();
  var loggingIn = new LoggingIn(homePage, loginPage);
  validCredentials.AreValid().Returns(true);

  //WHEN
  loggingIn.PerformUsing(validCredentials);

  //THEN
  response.Received().SetUpAsSuccessful();
  homePage.Received().RedirectTo();
}

The specification shows us that from the perspective of LoggingIn class, it's most comfortable to describe its behavior in the context of collaborators like Response, Page and Credentials that provide certain services in the form of methods. Thus, we have discovered three new abstractions (along with some methods they need to provide), that don't need to have concrete implementations in order to run specifications for LoggingIn class - we fill these abstractions with mocks, so that specs compile and run. When we're finished describing the LoggingIn object, we can proceed with describing real implementations for all the discovered abstractions the same way we did for LoggingIn class (by the way, the specifications for LoggingIn are always based on mocks to provide behavior isolation - we never change mocks to real objects in unit specs).

Principles

These are the main principles of Need-Driven Development:

  1. Start the development from the outermost layer (i.e. the inputs) and dig deeper and deeper, layer by layer, reaching the boundaries of the application
  2. Derive how interfaces should look like from what their clients need them to provide
  3. Derive not only interfaces (method signatures), but also protocols (e.g. which behaviors of discovered abstractions are expected by the class in terms of which we discovered them and how they should be handled)
  4. Think of the application as a web of collaborating objects sending messages to each other
  5. Mock only the types you own - mocks are primarily design tools, not isolation tools (although the latter is still somewhat significant)
  6. Avoid getters, try to follow "Tell, don't ask" advice most of the time
  7. Each test creates a need for either:
    • new implementation in the tested object
    • new collaborator to appear
    • new way of communicating with existing collaborator to appear (e.g. calling different method on a collaborator or a collaborator returning different result from already existing method)
  8. Distinguish between Value Types and "objects". Value types are merely a more intelligent and domain-oriented data, while "objects" are service providers.

Related Concepts

Lean

The need-driven development is closely related to Lean principles for pulling value from demand rather than pushing it from implementation. Thus, following the NDD leads to writing only the necessary code and avoiding adding features that aren't needed. This is because the only code that gets written is the one directly needed by code that already exists. The need for the first production code to exist are the acceptance tests, and those come directly from the customer. This way, NDD fits well into a cohesive value stream, starting with the customer and ending on the code itself. Thus, NDD is well aligned with lean principles.

"Needs interfaces" and "capabilities interfaces"

Another related concept is that of "Needs vs capabilities in interfaces", coming from the book Essential Skills for the Agile Developer. One important lesson from it is to always design interfaces (e.g. method signatures) according to client needs and what the client would use. After the second client is introduced that needs a different interface to the same services, the way to go is to provide it with its own Needs Interface and extracting the common logic in so called Capabilities Interface that both of these Need Interfaces use. At that time, these Need Interfaces most often become mere adapters or facades to the more general interface. The lesson from the authors of Essential Skills for the Agile Developers is compatible with the NDD philosophy, guiding us in our design and refactoring attempts.

Programming by Intention

If you remember my post about Programming By Intention and how it compares to TDD, then you probably already noticed the similarities between Programming By Intention and NDD. As a matter of fact, when I mentioned TDD in that post, I had NDD in mind.

Applicability

The technique is most useful when we have well-defined acceptance criteria and usually comes into play after creating and executing a failing acceptance test that defines the next increment of functionality we need to provide. Also, it's easier to apply when we know the high-level work-flows the application needs to support (usually the case of enterprise systems). Then we discover lower-level work-flows that result from the higher-level work-flows and, layer by layer, we reach the boundaries of the system.

Monday, 28 January 2013

Single method perspective vs single behavior perspective

Hi, today, I'd like to outline few differences between a technique called Programming By Intention and Test-Driven Development. I'm currently halfway through Essential Skills For The Agile Developer and the chapter on Programming By Intention made me revisit this fine technique and think it over again, especially in comparison to Test-Driven Development.

What's Programming By Intention?

On the book's page you can get the chapter on Programming By Intention for free and read it, but for those of you that like summaries - it's an outside-in approach to writing methods, that makes a distinction between so called "Sergeant" methods and "Private" methods. A "Sergeant" is a method that has the single responsibility of describing a high-level workflow and it delegates all the implementation details to "Private" methods. When beginning to write a piece of code, you start with a "Sergeant" and pretend that all the methods it needs to call already exist (although they usually don't). A simplified example of a "sergeant" for logging-in logic would be something like this:

public string LogIn(string userName, string password)
{
  string homePage = GetHomePageAddress();
  string loginPage = GetLoginPageAddress();
  string response = GetDefaultResponse();

  if(AreCredentialsValid(userName, password))
  {
    response = GetResponseWithSuccessfulLoginMessage();
    RedirectTo(homePage);
  }
  else
  {
    response = GetResponseWithLoginErrorsFor(userName);
    RedirectTo(loginPage);
  }
  return response;
}

As you can see, in the above example, we're passing what we can as method parameters instead of relying on fields. Also, there are almost no operators (even "new" and object access operator ".") - they're hidden inside the "private" methods to whom we delegate the work. These methods don't exist yet - we're just imagining how they should look to fit our needs. The next step is to generate skeleton bodies for "privates" (preferably using an IDE) and fill them in one by one (Of course, a "private" for this method might also be a "sergeant" with its own "privates"). A simple implementation example of a private method might look like this:

private string GetResponseWithLoginErrorsFor(string userName)
{
  return userName + " could not login. Invalid credentials";
} 

Programming By Intention produces code that is very readable - every work-flow step has its domain-specific name. Also, this technique is so powerful, because it separates specification perspective from implementation perspective and, by doing so, leads to a very high method cohesion. Thus, for example, it's very simple to refactor our exemplary "sergeant" later into something like this:

public Response LogIn(
  UserCredentials userCredentials,
  Page homePage, 
  Page loginPage)
{
  Response response = Response.Default();

  if(credentials.AreValid())
  {
    response.SetUpWithSuccessfulLoginMessage();
    homePage.RedirectTo();
  }
  else
  {
    response.SetUpWithLoginErrorsFor(userName);
    loginPage.RedirectTo();
  }
  return response;
}

and achieve class-level cohesion, although the decision whether to pay that cost right now or defer it for later is left to us. Thanks to this, Programming By Intention can be at best almost no-cost (in case of small programs that don't need any maintenance in the future), while at worst allowing us to defer the cost for later while introducing minimal technical debt (in case of more serious pieces of software that need cohesion on every level).

How does it relate to Test-Driven Development?

I must confess that this is the second most powerful technique I know for writing good and easy to read code, the only more powerful being Test-Driven Development. Whenever I am in a situation where for some reasons I cannot use TDD, I use Programming by Intention.

Now, let's examine the similarities between the two techniques first:

  1. Both are outside-in approaches (I'm talking here about the outside-in style of TDD, as it can also be used bottom-up, as e.g. Kent Beck prefers to use it), crafting APIs and method names from the perspective of their use
  2. Both are variants of the divide-and-conquer strategy
  3. Both are about specifying intention through code
  4. Both tend to lead to highly cohesive designs, although TDD demands both class-level and method-level cohesion, while Programming By Intention tends to demand method-level cohesion only (although class-level is easily achievable from there, as in the example above).

Now for the differences. The main one in my opinion is that Programming By Intention looks at writing code from a perspective of a method, while Test-Driven Development looks from the perspective of a behavior. This means several things:

  1. When implementing a single method (as in Programming By Intention), one does not see the whole context of its usage - only this one method. On the other hand, in TDD, when writing a unit-level spec, it usually begins with object instantiation and ends with observable result (either returning a result or call collaborating object's method)
  2. The method perspective also means that, when writing it using Programming By Intention, one has to consider the method as a whole - e.g. all required execution paths (ifs and elses) must be considered throughout the whole implementation process, since they're all usually added in one go. On the other hand, in TDD one only worries about the current path he's specifying with a test. Other paths are either already tied to other tests that will remind us when we unintentionally break them (so we don't have to think about them anymore), or on our TODO list (so we don't have to worry about them yet).

The second point is, IMHO, really important. Few days ago, I finally watched a presentation by Gojko Adzic and Dan North from BDD Exchange. Talking about accelerating Agile, Gojko and Dan stressed very heavily the importance of making a "measurable impact". I think that, when scaled down, this idea aligns well with TDD. The "measurability" of the "impact" we make with our executable specifications can be considered in two ways:

  1. For a single specification: watching the RED to GREEN transition is an act of measuring the impact - reaching the GREEN phase means that you now have something that was not present yet during the RED phase.
  2. For a suite of specifications: the comparison of already implemented specs vs the ones left on the TODO list are a way to measure "how much of the impact" was already made vs what's left.

Summary

Personally, I find many similarities between Programming By Intention and TDD, with Programming By Intention being lower cost (at least in the short run) and easier to learn, while TDD being more powerful and advanced. I dare to say that TDD is Programming By Intention on steroids. Also, I believe that learning Programming By Intention helps in in grasping mock-based Test Driven Development. Hence, if you are struggling with outside-in TDD using mocks, it's a good idea to train Programming By Intention first.

That's all for today, I hope you liked it and see you soon!

Sunday, 6 January 2013

Test Driven Development - objections, part 3

Welcome to part 3 of my post about TDD objections. Today's gonna be the last part of my comments on common objections to TDD. Again, I'm not an expert on these things - I'm merely writing down my thoughts and experiences, so any comment is appreciated! I'd love to know your objections or which ones you're most often getting into, how you deal (or dealt) with them etc.

Where did we leave the last time?

Ok, let's take on the next set of objections from the list:

  1. Not enough time
  2. TDD will slow us down, because we'll have to create a lot of additional code
  3. TDD will slow us down, because we'll have to maintain a lot of additional code
  4. We don't have the necessary skills or experience
  5. We don't have the necessary tools
  6. (This one is unit testing specific) There are already other kinds of tests, we don't need unit tests?
  7. It's very hard to perform with legacy code
  8. I already know how to write a testable code, TDD will not really improve my design
  9. We've got enough quality and we're doing fine without TDD
  10. My manager won't let me do TDD
  11. There's no "scientific proof" and enough research on whether TDD really provides a return of investment

The ones I'm gonna talk about today are the one marked with strong text. Let's go then!

8. I already know how to write a testable code, TDD will not really improve my design

Personally, I know several engineers who actually learned how to write testable code without TDD. They usually added unit tests after code and, over the time, learned to structure the code in such a way that it allows dependency injection, contains small number of private methods etc. If your mates are on this level, it's really not that that bad! However, I observed few deficiencies of such approach when compared to TDD:

  1. It is actually harder to learn how to write testable code without TDD. Also, many hours are wasted on refactoring before one grasps how to do it. In TDD, testability is built-in. So why choose the hard way?
  2. Test-first is often referred to as an "analysis step". You get the chance to take a step back and analyze what should the work-flow be, which behaviors have to be supported etc. Sure, one can use programming by intention to design classes and methods in an outside-in fashion, but when coding, you always see just the current method, or the current statement. In contrast, the unit spec/test forces you to consider whole class from object creation through invoking triggers up to getting a response. This perspective is more useful for analysis (and design) purpose and it is retained each time a new unit-level spec is written.
  3. The benefits of using TDD as a design-aiding tool are not only that you make testable classes that allow dependency injection. The benefits are also in making you focus on just what you actually need, without bothering you with what you don't. In TDD, you state your expectations one at a time, striving for the simplest solution. This leaves less room for over-generic, "just-in-case", framework-like overdesigned structures. Now, don't get me wrong, generic designs are a great and even necessary where genericity is needed and well justified, however, I believe that overdesign is almost as bad as underdesign.
  4. There are other things I have already written about that tend to happen in test-after approach

9. We've got enough quality and we're doing fine without TDD

This was actually responded to by James Grenning on his blog and the response is so good that I have really nothing to add. Go read it.

10. My manager won't let me do TDD

Putting things this way is usually a mental shortcut that might mean different things depending on the context. The most literal interpretation (like receiving a direct order not to do TDD) is almost never the case.

Sometimes, this is merely an excuse. Putting the blame or responsibility on the supervisor is one of the most popular ways of saying "I want someone to take the responsibility instead of me" and may be an example of Cover Your Ass Engineering.

Let me share with you my experience on the topic: in my first project, I thought my boss won't allow me to refactor. But I did - and in the end, no one questioned. In my second project, I thought my boss won't allow me to use smart pointers. But I did - and I was thanked for this. In my third project, I thought my boss won't allow me to use mock objects. But I did - and it was recognized. What's the moral? It's that often it is you who has the authority to make technical decisions. when you put responsibility on people who don't have the authority, they will hold back - this is natural. So go ahead, make the decision and take the responsibility. If it ends with success, take the gratitude. If it fails, take the blame. That's the best way to achieve something.

Sometimes, such complaint may also mean lack of support. In such situations, you can try out two steps that I know help dealing with this:

  1. Build your position - either internally (within the company) or externally (within the wider community) or both. This can be done by participating in open source projects, creating a blog, taking part in conferences, organizing trainings etc. When you are recognized in the community as an expert, your authority rises and people will get persuaded by you more easily.
  2. Instead of hoping your boss will create a plan and make you a part of it, create your own plan and show your boss how he can be a part of it. Show your boss what you want to achieve and what you need from them in order to be able to achieve this.

This whole section put in short is: "you're the one in charge. Believe it!".

11. There's not scientific proofs and enough research on whether TDD really brings any benefits

Oh really...

Let's put it this way - I could really try to convince somebody by listing the references, trying to analyze them, pick some arguments etc. and wait for the other person to say "Ok, I'm convinced" instead of something like "that doesn't convince me, because my case is special and these things do not apply". I could really try and do this.

But I won't. Why?

I won't, because I think this argument is usually raised in highly unfair ways. Ever wondered why anybody needs scientific proof for TDD while they didn't need any for daily stand-ups or four-week sprints, or retrospectives or any other agile or non-agile practices they've already incorporated? Can they show you the scientific research that led them to adopt all these techniques and methodologies?. If so, THEN let's talk about scientific proof for TDD (and it may be a very fruitful discussion). Otherwise, no.

From my observation, the "need for scientific proof" arises when someone simply doesn't want to adopt TDD and is using this argument as a mean to say "I don't wanna do this, unless you prove me that I have no other choice". There may be some reasons why this person doesn't want to try out TDD and they may be VERY good (and worth discussing), but the "scientific proof" argument itself is usually a facade.

Summary

This article ends the series of my comments on common TDD objections. At least for now. I hope you had as much fun reading this as I had writing.

See ya!