Telerik blogs
Welcome to Day Four of this “30 Days of TDD” series. If you’ve never done Test Driven Development or aren’t even sure what this "crazy TDD stuff” is all about than this is the series for you. Over the next 30 days this series of posts take you from “I can spell TDD” to being able to consider yourself a “functional” TDD developer. Of course TDD is a very deep topic and truly mastering it will take quite a bit of time, but the rewards are well worth it. Along the way I’ll be showing you how tools like JustCode and JustMock can help you in your practice of TDD. In the first two posts I explained at a high level what TDD is and the benefits it can bring to your software development practice. I also reviewed the principals of OOP and explained why good OOP practices are an important component of good TDD. These posts were designed to set the stage for this series in regards to goals and expectations and to create a common understanding of the basic techniques and practices needed for successful TDD. In the last post I started putting the pieces together and walked through creating out first test.

Previous Posts in this Series: Day Three – Your First Test

In the previous post I discussed the tools I will be using in this series; Visual Studio 2013 Preview as my development environment, NUnit as my unit testing framework and JustCode as my test runner. I also introduced a requirement and wrote my first test to capture that basics of that requirement. In this post I’ll demonstrate one of the most important concepts about TDD by writing just enough code to make the first test pass. I’ll then add more tests to introduce more functionality to our library.

Where We Left Off and What’s Next

In the last post I presented you with a single requirement:

“Create a library method that takes in a sentence and a single character as parameters. The method should return a number that indicates how many times the character appears in the sentence.”

 

From there I wrote a test to verify the “happy path” for this first requirement:

 1: using System;
 2: using System.Linq;
 3: using NUnit.Framework;
 4:  
 5: namespace ThirtyDaysOfTDD.UnitTests
 6: {
 7:     [TestFixture]
 8:  public class StringUtilsTest
 9:     {
 10:         [Test]
 11:  public void ShouldBeAbleToCountNumberOfLettersInSimpleSentence()
 12:         {
 13:             var sentenceToScan = "TDD is awesome!";
 14:             var characterToScanFor = "e";
 15:             var expectedResult = 2;
 16:             var stringUtils = new StringUtils();
 17:  
 18:             int result = stringUtils.FindNumberOfOccurences(sentenceToScan, characterToScanFor);
 19:  
 20:             Assert.AreEqual(expectedResult, result);
 21:         }
 22:     }
 23: }

(get sample code)

The test is ready, so the very first thing I want to do is try to run my test. This is a key part of the TDD workflow; write a test and then run that test immediately to see it fail. Most of the time it will (and should fail). The truth is a failing test, especially a failing that that has just been written for functionality that should not be in your application gives us a lot of information.

If I write a test for a feature that should not be in my code and it fails, it tells me a couple things. First of all it can help me verify (verify, NOT guarantee) that I’ve written the correct test. If the test were to pass immediately then there is a good chance I’m not testing the correct piece of functionality, as that functionality should not exist and the test should not pass. This may seem like a silly reason now as our requirement, test and code are very simple. But once you start working on larger and more complex systems it can sometimes be difficult to write a test that exposes the lack of a specific feature, especially if you are unfamiliar with the codebase or functionality of the application you’re working on.

The corollary to the first reason to run the test and see it fail is to ensure that I’m not duplicating someone else's work. In some cases, especially on larger projects, it possible that the same piece of functionality can somehow be scheduled twice. In this case a passing test will indicate that I’m about to duplicate someone else’s work. I’ve seen many developers in this situation who did not practice TDD forge ahead assuming they needed to write some code to implement a feature only to create a mess by having the same steps duplicated, usually in completely different ways and sometimes in the exact same methods as the original implementation. This can lead to a mess of unreadable, unreliable and un-maintainable code that nobody wants to touch in a hurry.

Before I can run my test I need to compile my solution and right away I can see my first problem (Figure 1):

image

Figure 1 – Compilation error

By definition if your solution does not compile your test has failed. In this case root of the failure is that the StringUtils class which I try to instantiate on line 16 in the example above does not exist.

Now we come to a bit of a cross roads in TDD; where do we create our class under test, in this case the StringUtils class. Our choices are to create it in the same project as our unit test (some developers actually like to create them in the same file) or do we create a new Class Library project (where our StringUtils class will eventually end of anyway) and create it there? There are benefits and drawbacks to each approach.

Strictly speaking, a major concept of TDD is the idea of not implementing functionality until it’s needed. This helps keep your code clean and free of unnecessary “clutter code” that can make an application more complex and difficult to understand that it needs to be. The idea is that even though you are developing a unit of functionality for the application, the application itself doesn’t need it yet. You don’t actually add the functionality to the application until you need it. When you need the class in your application (ideally in order to satisfy another test) you simply move it to the Visual Studio project you want it to be a part of. 

Conceptually I agree with this. But I also understand the argument, that for the sake of pragmatism, you can go ahead and create the new class in the Visual Studio project that it will end up in anyway. A benefit of this is that you are building the class where it lives and don’t have to worry about moving it later. Depending on your project methodology and how you “slice” features you may not only be responsible for creating the current piece of functionality, but also the user interface or upstream process that relies on it. If that’s the situation you can make a case that you know you’re going to have to move it sooner as opposed to later, so why not just put it there in the first place. Yet another benefit is that sometimes when code is developed in the test project its existence if forgotten and at some point in the future another developer ends up re-developing the functionality. This can be mitigated with good test and code naming standards and good project management, but it is still a risk.

The point is that there are good cases for both and you should do what works best for your particular project. In this case I’m going to create the class in my unit test project as it is not only conceptually the cleaner way to do it, but in this case it also happens to be the easiest and more pragmatic solution.

I need to add a new “cs” file to my project. If I’m using JustCode I can use the file template functionality by right clicking on the ThrirtyDaysOfTDD.UnitTests project and selecting JustCode –> Expand File Template option (or by using the Ctrl + Alt + Ins keyboard shortcut). Doing this presents me with JustCodes Available File Templates list (Figure 2)

image

Figure 2 – Available File Templates

As you can see there are a variety of templates available. You can also add your own templates, which I will address in a future post. For now though I want to select the C# Class option. I’ll replace the suggested class name with the name of my class, StringUtils as shown in Figure 3:

image

Figure 3 – Create new “Class” dialog

I click the OK button and JustCode creates a simple class for me:

 1: namespace ThirtyDaysOfTDD.UnitTests
 2: {
 3:  public class StringUtils
 4:     {
 5:  
 6:     }
 7: }

 (get sample code)

Creating the class was the simplest thing that might make my test pass, or at least get me past the current problem. When I attempt to recompile again I see I still have an issue as shown in Figure 4:

image

Figure 4 – New error

Based on this error the next thing I need to add is a method definition for FindNumberOfOccurneces in my StringUtils class. If I’m using JustCode I can put my cursor on the call to the method in the test (line 18 in the listing above) and press Ctrl + ~ which his brings up the JustCode Visual Aid Menu as seen in Figure 5:

image

Figure 5 – The JustCode Visual Aid menu

As you can see here, JustCode is offering to help us out by creating the FindNumberOfOccurences method for me:

 1: using System;
 2:  
 3: namespace ThirtyDaysOfTDD.UnitTests
 4: {
 5:  public class StringUtils
 6:     {
 7:  public int FindNumberOfOccurences(string sentenceToScan, string characterToScanFor)
 8:         {
 9:  // TODO: Implement this method
 10:  throw new NotImplementedException();
 11:         }
 12:     }
 13: }

(get sample code)

Compiling the application again succeeds, so it’s time to actually run the first test. I will be using the JustCode test runner for this series. If you don’t have JustCode you can download a 30 day trial here. If you do not want to use the JustCode test runner and have installed NUnit you will have access to the external NUnit test runner. Instructions for using that NUnit test runner can be found here.

Using the hotkey combination of Ctrl + Alt + K brings up the JustCode Unit test Window as shown in Figure 6.

image

Figure 6 – Unit Test Window

This window has two tabs; Explorer and Results. When the window opens the Explorer tab will be selected. The Explorer shows a list of all unit tests (including JavaScript unit tests written in Jasmine or QUnit) in a tree view grouped by Namespace (which usually ends up being by project) and Type. I have expanded the tree view all the way out to show our test. You can select other ways to organize this view using the “Group by” drop down list box.

To run a specific test or group of tests I can select the test or group (in this case the ThirtyDaysOfTDD.UnitTests namespace or the StringUtilsTests type) and click the single arrow icon on the far left of the window (shown in Figure 7)

 image

Figure 7 - Buttons to run a single group of test, all tests or debug tests

If I want to run all the tests I can click the double arrow to the right of the single arrow. If I want to debug a group of tests I can click the debug icon to the right of the double arrow.

For this example I’m going to run all the tests by clicking the double arrow (I can also use the keyboard short cut Ctrl + T, R) and JustCode will run my tests, showing the results in the Results tab of the Unit Test Window (Figure 8)

image

Figure 8 – Unit test run results

On the left side of the window I can see a listing of my unit tests. In this case I only have the one test, so that’s all that’s shown. I can also see some metrics about my last test run. In this case one of one tests ran and one failed. The big red X next to my test method name indicates that the test failed. The number to the right of the method name is time the test took to run, in this case three milliseconds.

The left side of the window gives us some information about why my test failed. In this case we can see that a System.NotImplementedException was thrown and according to the stack trace it happened on line ten of the StringUtils class. Here is the current listing of the StringUtils class:

 1: using System;
 2:  
 3: namespace ThirtyDaysOfTDD.UnitTests
 4: {
 5:  public class StringUtils
 6:     {
 7:  public int FindNumberOfOccurences(string sentenceToScan, string characterToScanFor)
 8:         {
 9:  // TODO: Implement this method
 10:  throw new NotImplementedException();
 11:         }
 12:     }
 13: }

As you can see, JustCode created the method for us, but we did nothing to implement it. Hence the exception being thrown on line ten. We need to write the simplest implementation that will make the test pass. Let’s review the test case again:

I want to pass in the sentence “TDD is awesome!” and the character “e” and my result should be two.

Based on this, the simplest implementation that will make this test pass is the following:

 1: using System;
 2:  
 3: namespace ThirtyDaysOfTDD.UnitTests
 4: {
 5:  public class StringUtils
 6:     {
 7:  public int FindNumberOfOccurences(string sentenceToScan, string characterToScanFor)
 8:         {
 9:  return 2;
 10:         }
 11:     }
 12: }

(get sample code)

Rerunning the test shows that this implementation, in spite of its simplicity, stratifies the current test (Figure 9)

image

Figure 9 – The test passes! Ship it!

Alright, set aside the absurdity of this example for a second. Clearly we are not ready to ship this feature; returning a hard-coded value of two is not what we were thinking when we created the requirement. But, if the test case we had was the only test case the requirement needed to support, we could say this feature is done.  More importantly, we would not want to develop any more on this feature. As developers it’s sometimes hard to know when to stop. Sometimes we can’t help ourselves from tinkering with code by adding features and functionality that aren’t in the specification because “They don’t know it, but they’ll need it.”

Well, what if they really don’t need it? It’s not uncommon for developers to get into the mindset that we know better than the users. But while we may be experts at software development, we are likely not at that level of expertise in other areas like approving loans, evaluating medical records or interpreting sales data. The business user is the expert in the business. Just like we wouldn’t like it if someone from accounting or sales starting telling us how to write our code, we shouldn’t be telling them what they need to do their jobs. The business knows what the business wants and needs, and if they don’t then they will figure it out and come back and ask for it. And we should wait for them to ask and then give them exactly what they ask for. Trying to anticipate needs creates a lot of “features” and code in the system that is never used and adds no value. In consulting we referred to this as “Gold Plating” which itself was simply a term meant to describe “a bunch of work the client didn’t ask us to do, so we don’t get paid for.”

The morale of the story; let the business decided what they need. Provide them exactly what they ask for. No more, no less. If your concerned about the business user making unreasonable requests, discuss it with them, but have an open mind and remember that ultimately the application your building is intended to serve their needs. If that doesn’t work, tie each feature they request to a dollar amount. It’s amazing how equating writing coding with spending money can help to control scope.

For our example it’s safe to say that the current test case is insufficient. To enhance the quality of our code, we’ll add another test case:

“I want to pass in the sentence ‘Once is unique, twice is a coincidence, three times is a pattern.’ and the character ‘n’ an my result should be 5”

The next step is to go ahead and write a new test. I’ll call this test ShouldBeAbleToCountNumberOfLettersInAComplexSentence and add it to the StringUtilsTest class as shown in this listing (starting on line 23):

 1: using System;
 2: using System.Linq;
 3: using NUnit.Framework;
 4:  
 5: namespace ThirtyDaysOfTDD.UnitTests
 6: {
 7:     [TestFixture]
 8:  public class StringUtilsTest
 9:     {
 10:         [Test]
 11:  public void ShouldBeAbleToCountNumberOfLettersInSimpleSentence()
 12:         {
 13:             var sentenceToScan = "TDD is awesome!";
 14:             var characterToScanFor = "e";
 15:             var expectedResult = 2;
 16:             var stringUtils = new StringUtils();
 17:  
 18:  int result = stringUtils.FindNumberOfOccurences(sentenceToScan, characterToScanFor);
 19:  
 20:             Assert.AreEqual(expectedResult, result);
 21:         }
 22:  
 23:         [Test]
 24:  public void ShouldBeAbleToCountNumberOfLettersInAComplexSentence()
 25:         {
 26:             var sentenceToScan = "Once is unique, twice is a coincidence, three times is a pattern.";
 27:             var characterToScanFor = "n";
 28:             var expectedResult = 5;
 29:             var stringUtils = new StringUtils();
 30:  
 31:  int result = stringUtils.FindNumberOfOccurences(sentenceToScan, characterToScanFor);
 32:  
 33:             Assert.AreEqual(expectedResult, result);
 34:         }
 35:     }
 36: }

(get code sample)

Now that our new test is written, we run it and see if fail (figure 10)

image

Figure 10 – The new test failed

The last time we had a failing test it was because the method under test didn’t have an implementation yet. This time our method has an implementation. But we are not getting the results we want, as shown in the details pane of the Unit Test Window (Expected: 5 But was: 2). The easiest way to make the test pass last time was to simply return two. This time I think the easiest thing to do is to actually implement an algorithm to count the occurrences of a character in a string. I’m going to implement this algorithm in the FindNumberOfOccurences method of the StringUtils class:

 1: using System;
 2:  
 3: namespace ThirtyDaysOfTDD.UnitTests
 4: {
 5:  public class StringUtils
 6:     {
 7:  public int FindNumberOfOccurences(string sentenceToScan, string characterToScanFor)
 8:         {
 9:             var stringToCheckAsCharacterArray = sentenceToScan.ToCharArray();
 10:             var characterToCheckFor = Char.Parse(characterToScanFor);
 11:  
 12:             var numberOfOccurenes = 0;
 13:  
 14:  for (var charIdx = 0; charIdx < stringToCheckAsCharacterArray.GetUpperBound(0); charIdx++)
 15:             {
 16:  if (stringToCheckAsCharacterArray[charIdx] == characterToCheckFor)
 17:                 {
 18:                     numberOfOccurenes++;
 19:                 }
 20:             }
 21:  
 22:  return numberOfOccurenes;
 23:         }
 24:     }
 25: }

(get sample code)

This may not be the most optimized or even best way to solve this problem. But right now that doesn’t matter; it’s the simplest and I’m looking for the simplest thing that makes my tests pass. If I run my tests again I can see that this algorithm has satisfied this need:

image

Figure 11 – The tests pass

In a future we will revisit this sample to demonstrate a few more advanced concepts of TDD and Refactoring. But right now I know that no matter what I do to this code in the future as long as these test pass, my code still satisfies these two test cases.

Summary

While the last two posts have covered a simple TDD case, they have covered a lot of ground. In this test I demonstrated how to take a new, failing test and write just enough code to make that test pass. I also explained why as a practitioner of TDD you want to let the requirements drive your test and let your tests drive your code to avoid extraneous features and code. In the next post I’ll be discussing the SOLID principals and how they play a big part in TDD.

Continue the TDD journey:

JustCode download banner image

JustMock banner


About the Author

James Bender

is a Developer and has been involved in software development and architecture for almost 20 years. He has built everything from small, single-user applications to Enterprise-scale, multi-user systems. His specialties are .NET development and architecture, TDD, Web Development, cloud computing, and agile development methodologies. James is a Microsoft MVP and the author of two books; "Professional Test Driven Development with C#" which was released in May of 2011 and "Windows 8 Apps with HTML5 and JavaScript" which will be available soon. James has a blog at JamesCBender.com and his Twitter ID is @JamesBender. Google Profile

Related Posts

Comments

Comments are disabled in preview mode.