Monday, December 2, 2019

Signed vs Unsigned Integers

Today I had cause to learn the difference between signed and unsigned integers (specifically in C#, but it looks to be a pretty standard topic). It turns out the difference is super simple: signed integers can be positive or negative, but unsigned integers must be positive. Since unsigned integers must be positive, they can hold larger numbers.

So I texted my brother just to say today I learned something, but that I thought the name could've been better. His reply: "Signed means it uses a sign and unsigned meaning the sign is always positive." and my mind is blown. That explanation makes perfect sense, but more importantly (to me) it might make it easier to remember this in the future.

Monday, November 4, 2019

PUT vs POST

The HTTP specification provides us with two verbs that confuse me pretty much every time I have to think about them: PUT and POST. Since I found a really good answer on SO for the difference between them and when to use each one, I thought I'd write up a blog post, include that link, and remind myself when I use each one.

Anyway, here's the answer I found that I like.

As far as when I use each one, I use POST when I'm creating new resources and don't want to specify their exact location (and I never specify the exact location when I send data to the server because I usually want the server or the database to generate an ID that will be used to identify the resource later). I use PUT when I'm replacing a known resource in its entirety. The HTTP spec defines that a PUT request must make no assumptions about what the caller wants, and should just take what was provided by the caller and apply it to the resource at that address. I use POST to update part of a known resource without replacing the whole thing.

Update: I've started using PATCH more often for updating parts of a resource without updating or replacing the entire thing. This is more semantically correct than using POST. I'll try to remember to update this with an actual code example of doing that once I figure out how I want it all to work.

Update 2: It turns out I've been using PATCH wrong. Per the spec PATCH requests are supposed to include a list of instructions on how the object should be patched. I found this article that helped me see the light, then these instructions for implementing PATCH "properly" in dotnet 5. I'll try to put up a full example when I get it all working and have time.

Update 3: I found time.

POST: /cars (to create a new car resource)
PUT: /cars/{id} (to replace the car resource with that ID)
POST: /cars/{id} (to update the car resource with that ID, but not replace it)
PATCH: /cars/{id} (to update some pieces of the car, but ignore everything that isn't present)

Thursday, September 12, 2019

Converting Strings to Enums

In my last post I showed how to use the DescriptionAttribute to specify a customized value for an Enum value and I promised to show how to convert back to an Enum. Here it is, the FromDescription string extension method. This time I did remember to get the link from the SO answer I got this from. Thanks, max!
   1: public static T FromDescription(this string description)
   2: {
   3:   var type = typeof(T;
   4:   if (!type.IsEnum)
   5:   {
   6:     throw new InvalidOperationException($"{description} does not match any of the enumeration values);
   7:   }
   8: 
   9:   foreach (var field in type.GetFields())
  10:   {
  11:     if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
  12:     {
  13:       if (attribute.Description == description)
  14:       {
  15:         return (T)field.GetValue(null);
  16:       }
  17:     }
  18:     else
  19:     {
  20:       if (field.Name == description)
  21:       {
  22:         return (T)field.GetValue(null);
  23:       }
  24:     }
  25:   }
  26: 
  27:   throw new InvalidOperationException($"{description} does not match any of the enumeration values);
  28: }

Now that we have the extension method we can invoke it on any string, like this:
var contactType = "Phone Call".FromDescription<ContactTypes>();

Wednesday, September 11, 2019

Converting Enums to Strings

Yes, I'm aware you can easily get the string value of an Enum by using .ToString(). And that's great in many circumstances. I recently came across one where it was important to us to be able to translate an Enum to a custom string that included spaces (which you can't do with an Enum's value). I should have bookmarked the SO answer where I got this code from, but I didn't (sorry, Original Author, whoever you are!).

This extension method takes advantage of the built-in DescriptionAttribute. We decorate our Enum values with the DescriptionAttribute and provide whatever we want, like this:
   1: public enum ContactTypes
   2: {
   3:   [Description("Phone Call")]
   4:   Phone,
   5:   Email,
   6:   Chat
   7: }

Once we've decorated the Enum values, we'll need the extension method to get the description we just provided.
   1: public static string ToDescription(this Enum value)
   2: {
   3:   var da = (DescriptionAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
   4: 
   5:   return da.Length > 0 ? da[0].Description : value.ToString();
   6: }

Now we just need to use the extension method on our Enum to get the description we specified. Let's say we have a class that looks like this:
   1: public class Interaction
   2: {
   3:   public string UserName { get; set; }
   4: 
   5:   public ContactTypes ContactType { get; set; }
   6: }

If we wanted to get "Phone Call", "Email", and "Chat" returned (depending on which one was assigned in the class), we'd use the ToDescription extension method like this:
   1: var contact = new Interaction { ContactType = ContactTypes.Phone, UserName = "engineer-andrew" };
   2: var contactType = contact.ContactType.ToDescription();

Since we didn't use the DescriptionAttribute on Chat and Email, they'll default to just use .ToString() so they'd return "Chat" and "Email", respectively.
That's all there is to it. I know it's kinda simple, but I've used it a couple of times and my rule is to blog about those things. In the next post I'll show how to go the other way (take a string and find its matching Enum value).

Thursday, June 27, 2019

Keyboard Events in Angular

It's not uncommon to have a form listen for keyboard events. In Angular there are some nifty shortcuts that make this process easy... ish. First, there are some (mostly undocumented) shortcuts you can use to listen for common key press events (like ).
<input matInput (keydown.enter)="doSomething($event)">

I've seen this used (and used it myself) in lots of places. Today I learned you can also listen for compound key press events.
<input matInput (keydown.ctrl.enter)="doSomething($event)">

This isn't documented so I don't know exactly which keys you can listen for or what kind of cross-browser compatibility there is, but I know the two I've got here, as well as the ones I've listed below, work in Chrome 75. If one of the keys you want to bind doesn't work (I tried keydown.add and keydown.+ and neither worked) you can go with a HostListener. These are pretty easy to use, but from what I can tell (and trust me, it would not be the first I'm wrong) they bind to the window and are bound from the initialization of the component until its destruction so I try to be careful with these. If you do want to go this route, here's how you can do it. This listener "hears" the plus sign on the keypad as well as the combination of shift and equal (for the plus sign on the qwerty part of the keyboard).
   1:  @HostListener('window:keydown', ['$event'])
   2:  keyEvent(event: KeyboardEvent) {
   3:    if (event.keyCode ==== KeyboardKeys.Add || (!!event.shiftKey && event.keyCode === KeyboardKeys.Equal)) {
   4:      this.doSomething();
   5:    }
   6:  }

There's an open request for documentation of key binding on Github if you want to go comment on it or see what other people are talking about it. Hopefully they get something put together sooner rather than later because it's much easier to use that than HostListeners.

Friday, June 21, 2019

How to Recursively Walk the Directory Tree

I can't believe I've never written this up before. Like, I literally can't believe it. I swear I did this a long time ago. Oh, well. Whatever.

I frequently need to search all the files in a directory for a specific text value and I always end up rewriting the same code to do it. Well, no more! Here it is. It's based on a sample from Microsoft.

   1: private static void WalkDirectoryTree(DirectoryInfo root)
   2: {
   3:  FileInfo[] files = null;
   4:  DirectoryInfo[] subDirectories = null;
   5: 
   6:  try
   7:  {
   8:   files = root.GetFiles("*.*");
   9:  }
  10:  catch (UnauthorizedAccessException e)
  11:  {
  12:   Console.WriteLine(e.Message);
  13:  }
  14:  catch (DirectoryNotFoundException e)
  15:  {
  16:   Console.WriteLine(e.Message);
  17:  }
  18: 
  19:  if (files != null)
  20:  {
  21:   foreach (var file in files)
  22:   {
  23:    if (File.ReadLines(file.FullName).SkipWhile(line => !line.Contains("search string")).FirstOrDefault() != null)
  24:    {
  25:     Console.WriteLine(file.FullName);
  26:    }
  27:   }
  28: 
  29:   subDirectories = root.GetDirectories();
  30: 
  31:   foreach (var subDirectory in subDirectories)
  32:   {
  33:    WalkDirectoryTree(subDirectory);
  34:   }
  35:  }
  36: }

Thursday, May 30, 2019

Reset Remote Git Repository to a Previous Commit

I've had to do this a few times and I've had to look it up each time. Next time I have to look it up, I can just come here.

Let's say you've committed some changes on a task branch, pushed them up to your remote repository, and completed a PR into a main-line branch. Then you realize something needs to get rolled back. How do you do that? It's pretty easy, actually.

git log --oneline (find the commit you want to go back to)
git reset --hard [commit hash] (reset the branch to the commit you want to go back to)
git push -f (force push the branch back up to the remote repository)

This doesn't delete the commit that was made by the PR, but it does essentially abandon it. You could always record that hash if you wanted and then move back to it later, but that's not what I wanted to do here so that's not what I showed how to do. Hopefully this helps someone other than me.

Thursday, May 2, 2019

Introducing TDD to Your Organization

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


We've heard from developers at several companies (large and small, publicly and privately held) that they're interested in implementing test-driven development, but they're not sure how to introduce the concept to their organizations. Below you'll find our preferred method for getting your fellow developers on board with you, then in our next post we'll explain how to convince management that it's a good idea.

First things first, you're a developer and you think test-driven development is the bee's knees, but your fellow developers don't. We need to get that out of the way right up front. If that's not you, then the approach we're about to provide may not be very helpful.

We've written before about some of the benefits of test-driven development and those are all good points to bring up to your fellow developers. But let's look at how we can target them a little more directly now. In our experience, all developers hate re-work as much as (or usually more than) any other part of their job so we're going to focus our pitch around re-work.

The first step in avoiding re-work is to really understand what you're building. It doesn't matter how rock solid your code is if you built the wrong thing in the first place. Test-driven development addresses this problem by forcing the developers to walk through the requirements of the product while they're writing their tests; before any code is written. During this phase developers can - and should - seek clarification from the product owner, which will lead to a product more in line with what the stakeholders want.

The next way we can avoid re-work is to reduce the number of defects in the code we do write. There are two ways test-driven development reduces the number of defects that make it to production. First, the fewer lines of code, the fewer opportunities exist for bad code and test-driven development reduces the number of lines of code by keeping the developer focused on delivering features that were actually requested. Second, when code is written using test-driven development there will be a complete suite of fully automated unit tests running at the end. Although this doesn't eliminate defects directly, it does ensure that what is written works as the developer expected. At this point we have code that works the way we expect it to, and does what the product owner wants it to.

The last step in avoiding re-work is actually part of the process of re-work. Despite our best efforts, most code will contain defects that get all the way to production and some developer down the road will need to fix them. Test-driven development protects those future developers by providing validation (through the complete automated test suite) that the bug fix doesn't introduce a new bug in a related area. However, test-driven development also has the added benefit of speeding up the bug fix process itself. When defects are reported from production they sometimes only exist under very particular circumstances that can be difficult to reproduce. When using test-driven development to fix a bug the first step is creating a test that proves the bug exists. Because our code is structured to facilitate testing we can more easily isolate reported defects. Once we've isolated the defect we can fix it, then run the test we already wrote to prove the bug is fixed. From there we can re-run the entire testing suite to make sure everything still works as expected and we're ready to move on from the defect and back to writing new code.

These are the reasons developers will be interested in trying out test-driven development, but you still need to create your argument in a way that gets their attention. We recommend something along the following lines:

"Using test-driven development will decrease re-work for all of us, giving us more time to focus on writing the cool new features we all like. The process can seem hard at first, but if we stick to it for six months we'll see fewer bugs making it to production, which will allow us to focus on more new features. Don't you hate having to switch gears in the middle of working on some cool new feature so you can go fix a bug in something you wrote a year ago, or even worse, something you didn't even write? Test-driven development can reduce the number of defects that make it to production AND allow us to more quickly fix the bugs that do make it out."

Most developers care about their code and they care about writing good code. Test-driven development is one more weapon they can include in their arsenal to write good code. You just have to help them see how test-driven development makes their lives easier and makes their jobs more fun. Hopefully, this approach will help you do that.

Wednesday, May 1, 2019

TDD from a Manager's Perspective

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business. This post in particular was actually written by a guest writer, Adam Johnson. He was a great manager I worked for previously and he graciously accepted my request to have him write a post for us.


Test Driven Development(TDD) is hard. It is hard from a developer perspective and it can be even harder from a manager perspective. Not only does a manager need to help drive TDD within team members, they also need to ensure the management chain above them understands the benefits of TDD.

Helping a team strive for test driven development is a slow process. Each developer will progress at their own pace. There will definitely be push back about "how hard it is" and "why are you making us do this" but being consistent in the message is key. It needs to be a collaborative effort when working with your team, not a top down mandate even if it is perceived as much. It will take numerous peer/pair programming sessions to help nudge team members in the right direction. It is important that proper peer programming is done, not a solo driver like the TDD expert on the keyboard for the majority of the session. Having a TDD champion that is a team member also helps drive the message home with the team. Working in concert with the manager, they can help other team members start down the long process of doing test driven development.

The long process not only occurs within the team, those outside the team also need to understand it is a long process. It really starts with the team's manager. If that person doesn't buy in to the benefits of TDD, it will be a long uphill battle trying to drive TDD within the team. After the immediate manager's buy in, the product owner and product manager will be the next to convince of the benefits of TDD. For a new team starting out with TDD, this means a slower velocity than what they previously had. In fact, any kind of metrics used by the team will change as it will take time for new members to get TDD. This is where the manager of the team needs to push back. It requires a manager willing to take all the heat to ensure the team is producing quality code and continually improving as they go through this process. There will absolutely be questions about why the team slowed down or why did this release not have as many features but once that release goes out and the total cost of ownership goes down due to the increased quality, the outsiders will start buying in.

The development manager needs to be consistent in the message to the team and the message to those outside of the team. Whether you are 1 day from releasing or 100 days, that hard process will get easier with each passing day. By delivering a consistent message and understanding that it is a long and hard process, the benefits will soon come to light not only for the team but for the entire organization.

Tuesday, April 30, 2019

TDD in JavaScript (Part 3)

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.

In our twoprevious posts on this topic we setup Jasmine in preparation for simple browser-based tic-tac-toe game, established our requirements for the game, and wrote our first test and first bit of functionality for the game. In this post we're going to work quickly through the iterative process of test-driven development.

Before we write a bunch of tests we're going to introduce the beforeEach and afterEach functions of Jasmine. As their names describe beforeEach and afterEach are executed before and after each spec (test) in a describe block, respectively. We're going to migrate some pieces from our first test into these functions so we can reuse them.

Changing our tests to be more concise, reusable, or more efficient is part of the very important refactoring step in test-driven development.
describe('selectBox', () => {
  beforeEach(() => {
    const elementToCreate = document.createElement('button');
    elementToCreate.id = 'topLeft';
    elementToCreate.innerHTML = ' ';
    elementToCreate.onclick = selectBox(elementToCreate);
    document.body.appendChild(elementToCreate);
  });

  it('should mark the box with the current players marker', () => {
    // arrange
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(element.innerHTML).toBe('X');
  });

  afterEach(() => {
    document.body.removeChild(document.getElementById('topLeft'));
  });
});
Now that we've refactored our unit test we can quickly write some more. We recommend writing the unit test blocks before writing all of the tests. Doing so helps us identify potential discrepancies in the requirements and gives us a better understanding of what we're being asked to code.

describe('selectBox', () => {
  beforeEach(() => {
    const elementToCreate = document.createElement('button');
    elementToCreate.id = 'topLeft';
    elementToCreate.innerHTML = ' ';
    elementToCreate.onclick = selectBox(elementToCreate);
    document.body.appendChild(elementToCreate);
  });

  it('should mark the box with the current players marker', () => {
    // arrange
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(element.innerHTML).toBe('X');
  });
  
  it('should become Os turn when X marks a box', () => {});
 
  it('should mark the box with O when the current player is X', () => {});
  
  it('should become Xs turn when O marks a box', () => {});

  it('should end the game when O marks three boxes in a row', () => {});

  it('should end the game when X marks three boxes in a column', () => {});

  it('should end the game when O marks three boxes diagonally', () => {});

  it('should end the game when all boxes are marked and neither player has won',
    () => {});

  afterEach(() => {
    document.body.removeChild(document.getElementById('topLeft'));
  });
});
While we were writing our tests we noticed that our requirements never specified how to start a new game, whether to keep track of how many wins each player had or how many games ended in a draw. We're going to move forward without these features, but it's good that we identified them now. If the product owner really wanted them, we would have been able to renegotiate the work we're doing to make sure we included the most important features in the first release.

Now that we have our tests started we can start writing them. For the sake of brevity, we've written them all and included them here.

describe('selectBox', () => {
  beforeEach(() => {
    const elementToCreate = document.createElement('button');
    elementToCreate.id = 'topLeft';
    elementToCreate.innerHTML = ' ';
    elementToCreate.onclick = selectBox(elementToCreate);
    document.body.appendChild(elementToCreate);
  });

  it('should mark the box with X when the current player is O', () => {
    // arrange
    const element = document.getElementById('topLeft');

    // act
    element.click(element);

    // assert
    expect(element.innerHTML).toBe('X');
  });

  it('should become Os turn when X marks a box', () => {
    // arrange
    player = 'X';
    const element = document.getElementById('topLeft');

    // act
    element.click(element);

    // assert
    expect(player).toBe('O');
  });

  it('should mark the box with O when the current player is X', () => {
    // arrange
    player = 'O';
    const element = document.getElementById('topLeft');

    // act
    element.click(element);

    // assert
    expect(element.innerHTML).toBe('O');
  });

  it('should become Xs turn when O marks a box', () => {
    // arrange
    player = 'O';
    const element = document.getElementById('topLeft');

    // act
    element.click(element);

    // assert
    expect(player).toBe('X');
  });

  it('should end the game when O marks three boxes in a row', () => {
    // arrange
    player = 'O';
    moves = ['', 'O', 'O', 'X', 'X', '', 'X', '', ''];
    
    // act
    element.click(element);

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when X marks three boxes in a column', () => {
    // arrange
    player = 'X';
    moves = ['', 'O', 'O', 'X', 'X', '', 'X', '', ''];

    // act
    element.click(element);

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>X Wins!</h1>');
  });

  it('should end the game when O marks three boxes diagonally', () => {
    // arrange
    player = 'O';
    moves = ['', 'X', 'X', 'X', 'O', '', '', '', 'O'];

    // act
    element.click(element);

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when all boxes are marked and neither player has won',
    () => {
      // arrange
      player = 'X';
      moves = ['', 'X', 'O', 'O', 'O', 'X', 'X', 'O', 'X'];

      // act
      element.click(element);

      // assert
      expect(  
        document.getElementById('gameStatus')
        .innerHTML).toBe('<h1>Cat\'s Game!</h1>');
  });
  
  afterEach(() => {
    document.body.removeChild(document.getElementById('topLeft'));
  });
});
All of our tests fail except the first one, which is perfectly normal. Now we write just a little bit of code, using our tests as our guides.

let moves = ['', '', '', '', '', '', '', '', ''];
let player = 'X';

function selectBox() {
  this.innerHTML = player;

  if (this.id === 'topLeft') {
    moves[0] = player;
  } else if (this.id === 'topMiddle') {
    moves[1] = player;
  } else if (this.id === 'topRight') {
    moves[2] = player;
  } else if (this.id === 'centerLeft') {
    moves[3] = player;
  } else if (this.id === 'centerMiddle') {
    moves[4] = player;
  } else if (this.id === 'centerRight') {
    moves[5] = player;
  } else if (this.id === 'bottomLeft') {
    moves[6] = player;
  } else if (this.id === 'bottomMiddle') {
    moves[7] = player;
  } else if (this.id === 'bottomRight') {
    moves[8] = player;
  }

  if (moves[0] === moves[1] && moves[1] === moves[2]) {
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[0] === moves[3] && moves[3] === moves[6]) {
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[0] === moves[4] && moves[4] === moves[8]) {
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else {
    document.getElementById('gameStatus').innerHTML = '<h1>Cat\'s Game!</h1>';
  }

  player = player === 'X' ? 'O' : 'X';
}
This function causes all of our tests to pass, but doesn't mean the game will work. We're only checking whether the top row is a win, or the left column, or diagonally from top left to bottom right. If someone marks all three boxes in the middle row they won't win. We'll need to go back and write more tests, then update our code for the tests to pass.

We've added the additional tests we needed and we also refactored some of our test code to include some helper methods to create and remove all of the buttons from the form for us for each test.

describe('selectBox', () => {
  function createButton(id) {
    const elementToCreate = document.createElement('button');
    elementToCreate.id = id;
    elementToCreate.innerHTML = ' '
    elementToCreate.onclick = selectBox;
    document.body.appendChild(elementToCreate);
  }

  beforeEach(() => {
    createButton('topLeft');
    createButton('topMiddle');
    createButton('topRight');
    createButton('centerLeft');
    createButton('centerMiddle');
    createButton('centerRight');
    createButton('bottomLeft');
    createButton('bottomMiddle');
    createButton('bottomRight');

    const gameStatusElement = document.createElement('div');
    gameStatusElement.id = 'gameStatus';
    document.body.appendChild(gameStatusElement);
  });

  it('should mark the box with X when the current player is O', () => {
    // arrange
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(element.innerHTML).toBe('X');
  });

  it('should become Os turn when X marks a box', () => {
    // arrange
    player = 'X';
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(player).toBe('O');
  });

  it('should mark the box with O when the current player is X', () => {
    // arrange
    player = 'O';
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(element.innerHTML).toBe('O');
  });

  it('should become Xs turn when O marks a box', () => {
    // arrange
    player = 'O';
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(player).toBe('X');
  });

  it('should end the game when O marks three boxes in the top row', () => {
    // arrange
    player = 'O';
    moves = ['', 'O', 'O', 'X', 'X', '', 'X', '', ''];
    const element = document.getElementById('topLeft');
    
    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when X marks three boxes in the middle row', () => {
    // arrange
    player = 'X';
    moves = ['', 'O', 'O', 'X', 'X', '', 'X', 'O', ''];
    const element = document.getElementById('centerRight');
    
    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>X Wins!</h1>');
  });

  it('should end the game when O marks three boxes in the bottom row', () => {
    // arrange
    player = 'O';
    moves = ['', '', 'X', 'X', 'X', '', 'O', '', 'O'];
    const element = document.getElementById('bottomMiddle');
    
    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when X marks three boxes in the left column', () => {
    // arrange
    player = 'X';
    moves = ['', 'O', 'O', 'X', 'X', '', 'X', '', ''];
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>X Wins!</h1>');
  });

  it('should end the game when O marks three boxes in the middle column', () => {
    // arrange
    player = 'O';
    moves = ['X', 'O', '', 'X', '', 'X', '', 'O', ''];
    const element = document.getElementById('centerMiddle');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when X marks three boxes in the right column', () => {
    // arrange
    player = 'X';
    moves = ['O', 'X', 'X', 'O', '', 'X', '', 'O', ''];
    const element = document.getElementById('bottomRight');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>X Wins!</h1>');
  });

  it('should end the game when O marks three boxes diagonally from top left to bottom right', () => {
    // arrange
    player = 'O';
    moves = ['', 'X', 'X', 'X', 'O', '', '', '', 'O'];
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>O Wins!</h1>');
  });

  it('should end the game when X marks three boxes diagonally from top right to bottom left', () => {
    // arrange
    player = 'X';
    moves = ['O', 'O', 'X', '', 'X', '', '', 'X', 'O'];
    const element = document.getElementById('bottomLeft');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>X Wins!</h1>');
  });

  it('should not end the game when not all boxes are marked and neither player has won', () => {
    // arrange
    player = 'O';
    moves = ['', 'X', 'O', '', 'O', 'X', 'X', 'O', 'X'];
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('');
  });

  it('should end the game when all boxes are marked and neither player has won', () => {
    // arrange
    player = 'X';
    moves = ['', 'X', 'O', 'O', 'O', 'X', 'X', 'O', 'X'];
    const element = document.getElementById('topLeft');

    // act
    element.click();

    // assert
    expect(document.getElementById('gameStatus').innerHTML).toBe('<h1>Cat\'s Game!</h1>');
  });
  
  afterEach(() => {
    document.body.removeChild(document.getElementById('topLeft'));
    document.body.removeChild(document.getElementById('topMiddle'));
    document.body.removeChild(document.getElementById('topRight'));
    document.body.removeChild(document.getElementById('centerLeft'));
    document.body.removeChild(document.getElementById('centerMiddle'));
    document.body.removeChild(document.getElementById('centerRight'));
    document.body.removeChild(document.getElementById('bottomLeft'));
    document.body.removeChild(document.getElementById('bottomMiddle'));
    document.body.removeChild(document.getElementById('bottomRight'));
    document.body.removeChild(document.getElementById('gameStatus'));
  });
});
With these updated tests we'll need to update our code, so we've done that as well.

let moves = ['', '', '', '', '', '', '', '', ''];
let player = 'X';


function selectBox() {
  this.innerHTML = player;

  if (this.id === 'topLeft') {
    moves[0] = player;
  } else if (this.id === 'topMiddle') {
    moves[1] = player;
  } else if (this.id === 'topRight') {
    moves[2] = player;
  } else if (this.id === 'centerLeft') {
    moves[3] = player;
  } else if (this.id === 'centerMiddle') {
    moves[4] = player;
  } else if (this.id === 'centerRight') {
    moves[5] = player;
  } else if (this.id === 'bottomLeft') {
    moves[6] = player;
  } else if (this.id === 'bottomMiddle') {
    moves[7] = player;
  } else if (this.id === 'bottomRight') {
    moves[8] = player;
  }

  if (moves[0] === player && moves[1] === player && moves[2] === player) {
    // top row
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[3] === player && moves[4] === player && moves[5] === player) {
    // middle row
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[6] === player && moves[7] === player && moves[8] === player) {
    // bottom row
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[0] === player && moves[3] === player && moves[6] === player) {
    // left column
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[1] === player && moves[4] === player && moves[7] === player) {
    // middle column
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[2] === player && moves[5] === player && moves[8] === player) {
    // right column
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[0] === player && moves[4] === player && moves[8] === player) {
    // top left to bottom right
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (moves[2] === player && moves[4] === player && moves[6] === player) {
    // top right to bottom left
    document.getElementById('gameStatus').innerHTML = '<h1>' + player + ' Wins!</h1>';
  } else if (!!moves[0] && !!moves[1] && !!moves[2] &&
    !!moves[3] && !!moves[4] && !!moves[5] &&
    !!moves[6] && !!moves[7] && !!moves[8]) {
      document.getElementById('gameStatus').innerHTML = '<h1>Cat\'s Game!</h1>';
  }

  player = player === 'X' ? 'O' : 'X';
}
And now our game of Tic Tac Toe should work exactly as we planned. The only part left is creating the actual markup for the game board. Since that's not really TDD we're going to show you what we used, but not go into any great detail about it. Here's our HTML file.

<html>
 <head>
  <script src="./src/tic-tac-toe.js"></script>
  <style>
   #board {
    height: 50%;
    width: 100%;
    position: relative;
   }

   button {
    font-size: 3em;
    width: 75px;
    height: 75px;
    position: relative;
    float: left;
   }

   #centerLeft, #bottomLeft, #gameStatus {
    clear: left;
   }

   #gameStatus {
    height: 10%;
    text-align: center;
   }
  </style>
 </head>
 <body>
  <div id="board"></div>
  <div id="gameStatus"></div>
  <script>createForm()</script>
 </body>
</html>
We're creating the controls for the form dynamically so we'll show you that part, too (the createForm() function called at the bottom of the markup you see up there).
It's important to note that we skipped doing TDD on the createForm and createButton functions for this guide, but if we were really developing a game like this we absolutely would have.


let buttons = ['topLeft', 'topMiddle', 'topRight', 'centerLeft', 'centerMiddle', 'centerRight', 'bottomLeft', 'bottomMiddle', 'bottomRight'];

function createButton(id) {
  const button = document.createElement('button');
  button.innerHTML = ' ';
  button.onclick = selectBox;
  button.id = id;
  return button;
}

function createForm() {
  const board = document.getElementById('board');
  for (let i = 0; i < buttons.length; i++) {
    const button = createButton(buttons[i]);
    board.appendChild(button);
  }
}
These two functions are included in our tic-tac-toe.js file for easier inclusion in the page.

That's the end of the guide on using test-driven development for your JavaScript. Happy coding!


Monday, April 29, 2019

TDD in JavaScript (Part 2)

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


In the previous post we setup Jasmine in preparation for a simple browser-based tic-tac-toe game. In this post we're going to actually write some tests and do some test-driven development for that game. First we need our requirements. Remember that in test-driven development our tests are written against the requirements and not the actual code. Without requirements we don't have any tests.

  1. In a game of tic-tac-toe we have a game board that has nine boxes arranged in a 3x3 pattern (3 columns and 3 rows)
  2. Our two players are Xs and Os.Xs go first.When X clicks on a box, that box is marked with an X and it becomes O's turn
  3. When O clicks on a box, that box is marked with an O and it becomes X's turn
  4. There are four possible outcomes to a game:
    • One player marks all three boxes in the same row
    • One player marks all three boxes in the same column
    • One player marks three boxes in a row diagonally
    • All boxes are selected and no player has achieved any of the other three outcomes
The rules are pretty straight forward so we should be able to generate some unit tests pretty easily from them.

A quick side not about Jasmine. Jasmine is a behavior-driven development framework, which implies a lot of things that aren't important to this post, but are important. We bring it up because our tests will be named differently than we've named them before. In Jasmine, each test is called a spec and the specs are grouped in logical blocks called describes. We recommend creating a new describe for each function you're going to test. Let's get set up to write our first spec.

I've created two new folders in tddjs: src and tests. In the tests folder, I'll create a new file called tic-tac-toe.spec.js. Because our application should be very simple we'll keep all of our specs in a single file. I'll also create a new file in the src folder called tic-tac-toe.js. Now that we have those two files (even though they're empty) we'll want to modify the SpecRunner.html file we got from Jasmine.

If you open SpecRunner.html you should see two sections marked with comments that indicate where you should include your source files and your spec files. Right now they likely reference files that we deleted earlier. We'll replace what's there with references to our two new files. SpecRunner.html now looks like this.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v2.8.0</title>

  <link rel="shortcut icon"
      type="image/png" href="lib/jasmine-2.8.0/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-2.8.0/jasmine.css">

  <script src="lib/jasmine-2.8.0/jasmine.js"></script>
  <script src="lib/jasmine-2.8.0/jasmine-html.js"></script>
  <script src="lib/jasmine-2.8.0/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/tic-tac-toe.js"></script>

  <!-- include spec files here... -->
  <script src="tests/tic-tac-toe.spec.js"></script>

</head>

<body>
</body>
</html>
Let's go ahead and open SpecRunner.html in our browser and take a look at what we have so far.



You should have something that looks very similar to this. We don't have any specs (tests) yet so nothing ran. That's fine. Now it's time to write our first test. Remember that in test-driven development our tests will always fail first.

We'll create a describe for a function we'll call selectBox in tic-tac-toe.spec.js.

describe('selectBox', () => {});
There's not much to see there because we still don't have our first spec written. We can add it easily enough.

describe('selectBox', () => {
  it('should mark the box with the current players marker', () => {});
});
Our Jasmine tests are named a little bit more plainly than our other types of tests. The language and constructs in Jasmine are designed to read like English. That is, our test should actually be read like this: "selectBox should mark the box with the current players marker". That allows us to easily correlate our tests with our requirements and share those results with our business customers if we want. This is a feature of behavior-driven development.

If you refresh SpecRunner.html in your browser you should now see a message indicating that you have a spec, it passed, but it has no expectations. That's great! It's not actually passing, but it's not failing either. Let's change our test to make it fail.

describe('selectBox', () => {
  it('should mark the box with the current players marker', () => {
 // arrange
 const element = document.getElementById('topLeft');
 element.innerHTML = ' ';
 
 // act
 element.click();
 
 // assert
 expect(element.innerHTML).toBe('X');
  });
});
This test simply tries to get an element from the page that has an id of topLeft. We make sure the element isn't already marked with anything by setting its innerHTML property to a non-breaking space. We click the element, then check to make sure its innerHTML property is now set to X. When we refresh SpecRunner.html we should now see that we have one failing test.


Jasmine tells us right away not only that we have a failing test, but also why our test is failing. In this case we can see that our test is failing because we tried to access the innerHTML property of an element that didn't exist. This is where things get a little bit more complicated. What we need to do now is create an element with the id we're looking for before our test runs.

describe('selectBox', () => {
  it('should mark the box with the current players marker', () => {
    // arrange
    const elementToCreate =  document.createElement('button');
    elementToCreate.id = 'topLeft';
    elementToCreate.innerHTML = ' '
    document.body.appendChild(elementToCreate);
    const element =  document.getElementById('topLeft');
 
    // act
    element.click();
 
    // assert
    expect(element.innerHTML).toBe('X');
  });
});
With this change we're using JavaScript to programmatically add a button to our document so we can access it and click on it later. Our test still fails because clicking on our button doesn't actually do anything yet.


This time we see a couple of new things on SpecRunner.html. First, we see that the reason for the failed test has changed. We now see that the failure is caused by our expectation not being met. This is good news because it means our test is running properly, but what we expect to happen isn't happening. The second thing we see that's new is there is now a button on the document in SpecRunner.html. You can see it there in the bottom left of the screenshot.

Let's add the functionality to give our new button a click event and tell it to invoke the function selectBox when it is clicked. We'll want to pass the element being clicked into the selectBox function. We'll also add a little bit of cleanup to remove the button from the document when our test finishes. This helps keep SpecRunner clean, but it will also have greater implications as we add more tests.

describe('selectBox', () => {
  it('should mark the box with the current players marker', () => {
    // arrange
    const elementToCreate =  document.createElement('button');
    elementToCreate.id = 'topLeft';
    elementToCreate.innerHTML = ' '
    elementToCreate.onclick = selectBox(elementToCreate);
    document.body.appendChild(elementToCreate);
    const element =  document.getElementById('topLeft');
 
    // act
    element.click(element);
 
    // assert
    expect(element.innerHTML).toBe('X');

    // cleanup
    document.body.removeChild(elementToCreate);
  });
});
Our test still fails, but this time it's failing for exactly the right reason: there is no selectBox function. Also notice that the button is not showing up anymore.


Since we're going to do this iteratively (like we're supposed to) we can go ahead and add a selectBox function to our tic-tac-toe.js file.

function selectBox() { }
We're back to having a failing test because our expectation is not being met. That makes sense because nothing is happening in our actual code yet. Now we can add the code that updates the innerHTML property of the element that was clicked.

function selectBox(element) {
    element.innerHTML = 'X';
}
Our test is passing now! Astute readers will notice that our code doesn't currently correctly implement our requirements, but that's OK for right now. Right now we've written a single test that tests a single piece of code and that test passes.

We're going to take another break here. At this point we know how to setup Jasmine, write our first test, and get it to pass. In the next installment we'll add more tests and try to get closer to having a working game of Tic Tac Toe.


Sunday, April 28, 2019

TDD in JavaScript (Part 1)

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


We've heard from some front-end developers that test-driven development isn't possible or useful for them. We (obviously) couldn't disagree more so we decided to put together a quick guide to using test-driven development to create a pure JavaScript application. We prefer the Jasmine testing framework so that's what we're going to demonstrate here.

First off, if this is the first time you've heard of Jasmine you might want to check out their official site (the link is in the Resources section at the bottom of this post).

Whether you want to check out their site or not, you're going to need to go there to get the framework. Don't worry, it's a small download and they have it available in .zip and .tar format. You can just go straight to the link in the Resources section if you don't want to browse their site.

To keep things simple I created a directory called "tddjs" on my desktop and unzipped the file I downloaded straight into that directory. Here's what my folder looks like.


Their download now includes some sample code of their own (in the src and spec folders), but we don't need that. For this guide we are only interested in keeping the contents of the lib/jasmine-2.8.0 directory and the SpecRunner.html file in the root. I'm going to go ahead and delete the other files. Here's what my folder looks like now.

Looking into the lib/jasmine-2.8.0 folder we see 6 files:

boot.js

console.js

jasmine.css

jasmine.js

jasmine-favicon.png

jasmine-html.js

jasmine.js contains the actual Jasmine testing framework, jasmine-html.js contains some functions that help format the page, and boot.js initializes Jasmine.

Now that we have Jasmine ready we need some code to test. *record scratch* Oops. We need some tests. We're going to create a simple tic-tac-toe game. This should allow us to focus on the functionality we expect instead of getting bogged down in how things look. This is actually a really good stopping point so we're going to break here. We should be able to wrap things up in our next post.

Resources

Official Jasmine Home Page

Download Jasmine


Saturday, April 27, 2019

Do I Still Need QA If I Use TDD?

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


One of the most common misconceptions we hear about test-driven development (TDD) - and thorough unit testing in general - is that once it is implemented you no longer need to do any quality assurance (QA). We'll explain in this post a few reasons why you definitely still want to do QA and probably some user acceptance testing (UAT) as well.

The primary reason you still want to make sure you're doing QA when you're doing TDD is that TDD focuses on testing the units of code. If you read our guide you may recall that units can be as small as a single method or as large as an entire class (encompassing many methods that work together, but with a single purpose). But even when all of our tests pass and we've tested every possible permutation and combination of variables we still don't know whether our software actually works. All we know is that the individual parts work in isolation from each other.

Think about a few parts of a car that work together: the ignition switch, the starter, and the engine. Our unit tests have verified that the ignition switch engages when the key is in the proper position. They've verified that given an electrical current, our starter engages the flywheel. They've verified that when the flywheel is engaged the engine starts. We know that all of the individual parts of the car are working properly in isolation, so our QA process is to essentially make sure we've assembled those pieces properly. These tests are generally referred to as integration or end-to-end tests.

Thinking in terms of code again our unit tests will verify that a function works as expected with a given set of inputs. An integration test will verify that the button on the user interface actually invokes that function with the expected set of inputs. An end-to-end test will verify that when a user visits the page and provides all of their information and clicks the button, a new login is created and they are able to sign in to the application.

A secondary benefit of formal QA is simply getting a "second set of eyes" on the functionality. When we use TDD to write our tests and our code, it is most often the same person doing both. So when the developer delivers that software she is the only person who has really looked at what it does and how (and whether) it works. Having a QA team review it is a good safety check to make sure the developer didn't miss anything.

Although the use of TDD will significantly reduce the number of bugs, it will very rarely eliminate them completely. Having a QA person or team review the delivered software against the acceptance criteria is a really important step to make sure the maximum amount of bugs has been discovered prior to a demo.

Finally, in Agile (Scrum in particular) it is important to remember that the development team is responsible for delivering the code, it is up to the product owner (and stakeholders) to decide when to release that code for consumption. It is highly recommended that at least some testing is performed by at least a small subset of end users (this could mean stakeholders or a limited beta release or even using feature flags to enable the new features for a very small subset of users) before the product is made widely available. Just as QA looks for different things than unit tests, end users will look for different things than QA. Ideally, by the time our product goes to demo at the end of our sprint we will have identified and resolved technical bugs ("I clicked the button and nothing happened"). But the UAT process will help us identify whether we had some unknown issues in our requirements or somewhere else along the way ("Why can I choose to withdraw pennies from an ATM that only has $20 bills in it?").

Although TDD is a wonderful tool to reduce defects, it is only one tool in the overall testing belt. Just like the hammer is good for hammering in nails, we need to use other tools for other parts of the job.


Friday, April 26, 2019

Who Needs Test-Driven-Development?

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.

Recently, a seasoned developer asked a question of ThoroughTest that we wanted to answer for the community at-large: "Do really good developers need test-driven development?" The short answer is yes, absolutely, unequivocally, definitely. Unfortunately, that's not very convincing so we'll try to explain exactly why we feel that way.

First, let's address the elephant in the room on this one. ThoroughTest is a business and our business is training and certifying organizations and individuals on the concepts and processes of test-driven development. The short version is that we stand to make money when more people get on board with test-driven development. There's no getting around that. And although we want you to become a certified test-driven development practitioner with us, the truth is we feel passionately about helping people write quality software and we strongly believe that using test-driven development is one of the best ways to make that happen. Now that we've cleared that up, let's talk about why even really good developers need test-driven development.

If you are a really good developer and you don't work on a team of other developers and you always write your own requirements and you work for yourself at your own company and you don't care about realizing all the advantages of test-driven development then no, you don't need test-driven development. If, however, you live in the real world then the question isn't really about whether you as an individual need (or would benefit from) test-driven development. Test-driven development is an organization-wide effort that is most successful when everyone from the highest executive to the lowest intern is on board with it.

Organizational commitment aside, really good developers benefit from test-driven development just as much as brand new developers. We discussed five benefits of test-driven development in an earlier post and really good developers would still reap all of those benefits. Even if a really good developer already feels like she doesn't usually have problems understanding requirements, the process of discussing those requirements with the product owner in the context of how to test them will result in better communication between the developer, product owner, and even the QA team.

Since test-driven development isn't about making individual developers better, there's no point on the "good developer" scale where the benefits would be lost. One of the benefits of utilizing test-driven development that we didn't mention in our previous post was the stability of the code base going forward. As developers, we all know that there will almost definitely come a day when our beautiful, perfect, pristine code will have to be modified and we may not be the person who gets to make those modifications. By using test-driven development initially we'll be confident that future changes won't break what we've spent so much time creating, even if those future changes are made by a developer who is not "really good".

If you're considering making the transition to test-driven development, but you're not sure whether you personally would benefit from it, try to remember that this method of writing software is designed to benefit the entire team, division, and organization. So yes, even really good developers need test-driven development.


Thursday, April 25, 2019

Five Good Reasons to Use Test-Driven Development

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


When it comes to test-driven development, many developers want to know why it is better to write tests before code instead of the other way around. This is a great question and is truly a stumbling block for a lot of people when it comes to climbing on board the test-driven development train. The main way we hear this question phrased is "Why can't I just write my tests after my code is complete so I know what I'm testing?" Of course you can write your code first, and there isn't necessarily anything wrong with that approach. It's just not as beneficial as writing your tests first. In this article we'll discuss the five major reasons why writing tests first is more advantageous than writing code first.

A Better Understanding


The first benefit you'll see when writing your tests first is a more thorough understanding of the requirements. This is sort of an added bonus of test-driven development. It has been our observation that when developers write the unit tests first, based on the requirements, they are more likely to identify shortcomings in those requirements, and are also more inclined to seek clarification. Since the developers haven't written any code yet, they aren't worried about scrapping anything as the requirement gets clarified.

Write Less Code


The second benefit developers encounter is that they write less actual code. In our experience, most developers are willing to dive right in and start writing the best, coolest, most extravagant code they can imagine. As long as what they end up with satisfies the requirements they don't have anything to worry about. The problem with that approach is that the neat bells and whistles we tend to spend our time on aren't necessarily important to the product owner (or end users). When we use test-driven development all the way through the process, we see developers spend time only writing code that satisfies requirements. Bells and whistles can still be added, but they are more deliberately added by creating requirements around them. This actually goes back to the first benefit of test-driven development. When developers understand what the product owner wants it is easier to stay focused on that goal and deliver something that satisfies everyone in the allocated time.

Better Design


The third benefit of test-driven development is that it helps enforce SOLID design principles, which will almost always lead to better code. Test-driven development usually requires some form of dependency injection (D: Dependency Inversion), provide the developer an opportunity to keep classes focused (S: Single Responsibility and I: Interface Segregation), and helps enforce the idea of swapping out concrete implementations at runtime (L: Liskov Substitution Principle). Writing better, more extensible, more maintainable code should always be the goal of the development team, and test-driven development will inherently work to that end.

Faster Development


This brings us to the fourth significant advantage of test-driven development: timeliness. If you've read our guide you'll know that test-driven development goes pretty hand-in-hand with Agile development methodologies. One of the 12 principles of Agile is to "deliver working software frequently" and test-driven development helps with that. When developers are able to understand the requirements better and stay focused on what the product owner wants delivered, they spend less time adding features no one asked for, which shortens the development cycle, which allows more working software to be delivered more frequently.

Faster Delivery


The fifth advantage of test-driven development goes hand-in-hand with the fourth: the overall release cycle is shorter. In addition to shorter development cycles organizations will see shorter QA stages with fewer bugs discovered, shorter user acceptance testing stages, and fewer bugs reported in production. Since the developers and product owner clarified the requirements before any code was written, the QA team has a clearer understanding of what the final software should do. This allows them to create much more targeted tests. There is also the added bonus that QA will find less nuisance bugs (e.g. a system failure when a field is left empty), which requires less re-work and allows the QA team more time to find major bugs. When the product passes QA and goes out for user acceptance testing, the product owner isn't surprised by anything she didn't ask for in the development process. Since the developers received clarification at the beginning of the process, there is less room for ambiguity and fewer reports of issues from users. Finally, once the code does reach production, more bugs have been found throughout the development and approval process, which means there are fewer bugs actually released into the wild. In addition to better uptime and customer satisfaction, this also means developers won't have to stop work on new features to fix user-reported bugs in production code.

So can't these benefits be realized by writing tests after the code is complete? Since all of the advantages we've listed here build off the first one, and that first one provides clearer requirements, there's really no way to duplicate these benefits if we write our code first. Just as it is better to have coded unit tests than to not have coded unit tests, it is better to use test-driven development to write those coded unit tests than it is to write them after the code is complete. Although there are many more smaller benefits of writing your tests first, we feel these five provide the most compelling argument in favor of test-driven development.


Wednesday, April 24, 2019

Test-Driven Development in SQL Server

This blog post was originally published on the ThoroughTest website, back when that was a thing. As a co-founder and primary content contributor for ThoroughTest, I absolutely own the rights to this post and the source code to which it refers. I intend to reproduce each blog post here on my personal blog since the company is no longer in business.


We've heard from a few developers working with SQL Server that they're having a hard time justifying test-driven development of their "code" when a lot of their logic resides in stored procedures. That's a valid point: why use test-driven development (or write unit tests at all) if you can't cover the entire codebase? Well, that's a larger topic that we may discuss in a future post, but for now we can resolve the issue by introducing a method for unit testing SQL Server objects with the tSQLt testing framework.

For starters, you can check out the official documentation of tSQLt by visiting their website. We'll use the rest of this post to show a few simple ways we can do test-driven development using the framework. Keep in mind that this is a limited overview of what tSQLt provides and can do so just because you don't see a solution for your scenario here doesn't mean they don't have one. Review their official docs to get a more thorough understanding of what's possible.

For these examples we'll create a database called CookBook. You can create it on any instance of SQL Server (including Express) and any version since 2005. This database will be a repository for our recipes so we'll create two tables and one join table: Recipe, Ingredient, and RecipeIngredient. You can use the scripts below to follow along.

CREATE TABLE Recipe
  (
     Id          INT IDENTITY(1, 1),
     Name        VARCHAR(100) NOT NULL,
     DateCreated DATETIME2 DEFAULT GETDATE()
  )

ALTER TABLE Recipe
  ADD CONSTRAINT PK_Recipe_Id PRIMARY KEY CLUSTERED (Id)

CREATE TABLE Ingredient
  (
     Id          INT IDENTITY(1, 1),
     Name        VARCHAR(100) NOT NULL,
     DateCreated DATETIME2 DEFAULT GETDATE()
  )

ALTER TABLE Ingredient
  ADD CONSTRAINT PK_Ingredient_Id PRIMARY KEY CLUSTERED (Id)

CREATE TABLE RecipeIngredient
  (
     Id           INT IDENTITY(1, 1),
     RecipeId     INT NOT NULL,
     IngredientId INT NOT NULL,
     Amount       DECIMAL(4, 2) NOT NULL,
     Measurement  VARCHAR(100) NOT NULL
  )

ALTER TABLE RecipeIngredient
  ADD CONSTRAINT PK_RecipeIngredient_Id PRIMARY KEY CLUSTERED (Id)

ALTER TABLE RecipeIngredient
  ADD CONSTRAINT FK_RecipeIngredient_Ingredient FOREIGN KEY (IngredientId)
  REFERENCES Ingredient(Id)

ALTER TABLE RecipeIngredient
  ADD CONSTRAINT FK_RecipeIngredient_Recipe FOREIGN KEY (RecipeId)
  REFERENCESRecipe(Id) 
Now that we have our table structure we want to create a stored procedure that gets all the ingredients for a specific recipe in our cook book. We want to see the Ingredient Name, Amount, and Measurement for each item in the recipe and we want to specify the recipe by name. We know our requirements so now we can write our tests. Before we can start writing tests that will actually work we need to install the tSQLt framework, which is pretty simple to do. We just download the zip file, unzip it, and run SetClrEnabled.sql followed by tSQLt.class.sql on your database. Now that the framework is installed it's time to write a unit test.

The first thing we want to do is create a test class, which will be used to group our tests together. We prefer to create test classes with the name of the stored procedure followed by ".Tests" to be clear what's being tested and that it is a test class. Since a test class is just a schema we should see a new schema created after we take this action.

EXEC tsqlt.Newtestclass 'GetIngredientsByRecipeName.Tests' 
Now we have a test class for our stored procedure (which you'll notice we plan to name GetIngredientsByRecipeName) and we can create our first actual test. Tests in tSQLt are just stored procedures named a specific way so we can use our existing TSQL skills to create our tests. The first thing we need to do in our test stored procedure is setup our data, which we can do by having tSQLt create fake tables for the tables we'll need. Once we have our fake tables we can populate them with fake data. By using fake data we'll be sure that our tests will always pass without having to worry about what recipes are actually in the database. Here's what we have so far:

CREATE PROCEDURE
[GetIngredientsByRecipeName.Tests].[Test that all ingredients are returned]
AS
  BEGIN
      -- Arrange
      EXEC tsqlt.FakeTable
        'Recipe'

      EXEC tsqlt.FakeTable
        'Ingredient'

      EXEC tsqlt.FakeTable
        'RecipeIngredient'
  END 
The FakeTable procedure opens a transaction, renames the table being passed, then recreates a table with the same name and structure, but without any constraints, defaults, or triggers. After the code above runs, all three tables will be empty shells of their normal selves, allowing us to populate whatever data we want into them. We'll populate our fake tables with some fake data so we can anticipate the results of our stored procedure.

INSERT INTO Recipe(Id,Name) VALUES(1,'Grilled Cheese')

INSERT INTO Ingredient (Id, Name)
VALUES(1, 'Butter'), (2, 'Cheddar Cheese'), (3, 'White Bread')

INSERT INTO RecipeIngredient (RecipeId, IngredientId, Amount, Measurement)
VALUES(1, 1, 1, 'Tbsp'), (1, 2, 1, 'Slice'), (1, 3, 2, 'Slices')

INSERT INTO Recipe (Id, Name) VALUES(2, 'Quesadilla')

INSERT INTO Ingredient (Id, Name) VALUES(4, 'Tortilla')

INSERT INTO RecipeIngredient (RecipeId, IngredientId, Amount, Measurement)
VALUES(2, 4, 1, 'Tortilla'), (2, 2, .5, 'Cups')
Note: Even though we could normally exclude inserting values into the Id fields of Recipe and Ingredient (because they are identity fields and should automatically get the next number) we have to explicitly include them in our test because the fake tables are created without identity fields.

Now we have two recipes' worth of fake data and we know what we expect our stored procedure to do. We're going to want to compare the results of our stored procedure to what we expect so we'll create a temp table to store the results and a temp table containing our expected results.

CREATE TABLE #temp
  (
     IngredientName VARCHAR(100),
     Amount         DECIMAL (4, 2),
     Measurement    VARCHAR(100)
  )

CREATE TABLE #expected
  (
     IngredientName VARCHAR(100),
     Amount         DECIMAL (4, 2),
     Measurement    VARCHAR(100)
  )

INSERT INTO #expected
VALUES('Butter', 1, 'Tbsp'), ('Cheddar Cheese', 1, 'Slice'), ('White Bread', 2, 'Slices')

-- Act
INSERT INTO #temp
EXEC GetIngredientsByRecipeName 'Grilled Cheese' 
Finally, we'll actually compare the results of the two tables by executing the AssertEqualsTable procedure from the tSQLt framework. This procedure compares the contents of two tables for equality. Since we want to confirm multiple values across multiple rows, this option makes the most sense for us.

-- Assert
EXEC tsqlt.AssertEqualsTable '#temp', '#expected' 
Now we can create the stored procedure and run it using the Run procedure from tSQLt and passing either the test class or the test name to the procedure as a parameter. We'll use the test class name because going forward we'll want all of our tests to run whenever we make a change to our stored procedure. This is a good habit to get into now.

EXEC tsqlt.Run 'GetIngredientsByRecipeName.Tests' 
Good news; the test failed! There is no stored procedure named GetIngredientsByRecipeName yet so the test failed. We've established our first Red step in test-driven development! Create the procedure, but don't put anything in it yet.

CREATE PROCEDURE GetIngredientsByRecipeName
(
    @RecipeName VARCHAR(100)
)
AS
BEGIN
    PRINT 'Called'
END 
Run the test again and look at the output. This time, instead of getting an error message that it "could not find stored procedure 'GetIngredientsByRecipeName'" we see "(Failure) Unexpected/missing resultset rows!" and then a description of how the two tables failed to match. For more details on how to read this output, check out the tSQLt docs for AssertEqualsTable.

Let's finally modify our stored procedure to do what we want it to do: get the ingredients for the specified recipe.

CREATE PROCEDURE GetIngredientsByRecipeName
(
    @RecipeName VARCHAR(100)
)
AS
BEGIN
    SELECT
         Ingredient.Name
        ,RecipeIngredient.Amount
        ,RecipeIngredient.Measurement
    FROM Ingredient
    INNER JOIN RecipeIngredient
        ON Ingredient.Id = RecipeIngredient.IngredientId
    INNER JOIN Recipe
        ON RecipeIngredient.RecipeId = Recipe.Id
    WHERE  Recipe.Name = @RecipeName
END 
When we run our test one more time we see that it passed. Now we have our Green step so we'll review our stored procedure for any opportunities to improve. We don't see any so our Refactor step is complete without any changes.

We've got a new requirement that ingredient amounts should be summed up when the same ingredient is in the same recipe with the same measurement more than once. First we'll write the test:

CREATE PROCEDURE
  [GetIngredientsByRecipeName.Tests].
   [Test that ingredient amounts are summed]
AS
BEGIN
    -- Arrange
    EXEC tsqlt.FakeTable 'Recipe'

    EXEC tsqlt.FakeTable 'Ingredient'

    EXEC tsqlt.FakeTable'RecipeIngredient'

    INSERT INTO Recipe (Id, Name) VALUES(1, 'Salt Soup')

    INSERT INTO Ingredient (id, Name)
      VALUES (1, 'Salt'),
        (2, 'Chicken Broth'), (3, 'Carrots'), (4, 'Leather Boot')

    INSERT INTO RecipeIngredient(
      RecipeId,
      IngredientId,
      Amount,
      Measurement
    )
      VALUES (1, 1, 1, 'Tbsp'), (1, 2, 10, 'Cups'),
        (1, 3, 10, 'Carrots'), (1, 4, 1, 'Boot'), (1, 1, 16, 'Tbsp')

    CREATE TABLE #temp
      (
         IngredientName VARCHAR(100),
         Amount         DECIMAL (4, 2),
         Measurement    VARCHAR(100)
      )

    CREATE TABLE #expected
      (
         IngredientName VARCHAR(100),
         Amount         DECIMAL (4, 2),
         Measurement    VARCHAR(100)
      )

    INSERT INTO #expected
      VALUES ('Salt', 17, 'Tbsp'), ('Chicken Broth', 10, 'Cups'),
        ('Carrots', 10, 'Carrots'), ('Leather Boot', 1, 'Boot')

    -- Act
    INSERT INTO #temp
    EXEC GetIngredientsByRecipeName 'Salt Soup'

    -- Assert
    EXEC tsqlt.AssertEqualsTable '#temp', '#expected'
END 
Then we'll run all of the tests in the test class:

EXEC tsqlt.Run 'GetIngredientsByRecipeName.Tests' 
We get an exception: "(Failure) Unexpected/missing resultset rows!". We update the stored procedure:

ALTER PROCEDURE GetIngredientsByRecipeName
(
    @RecipeName VARCHAR(100)
)
AS
BEGIN
    SELECT
         Ingredient.Name
        ,SUM(RecipeIngredient.Amount)
        ,RecipeIngredient.Measurement
    FROM Ingredient
    INNER JOIN RecipeIngredient
        ON Ingredient.Id = RecipeIngredient.IngredientId
    INNER JOIN Recipe
        ON RecipeIngredient.RecipeId = Recipe.Id
    WHERE  Recipe.Name = @RecipeName
    GROUP BY
       Ingredient.Name
      ,RecipeIngredient.Measurement
END 
And finally we run all of our tests again:

EXEC tsqlt.Run 'GetIngredientsByRecipeName.Tests' 
This time our test summary shows that we have two tests that ran and both of them passed.

Stored procedures are an important part of database programming and sometimes play a large role in applications and architecture. Using the tSQLt framework we can realize the advantages of test-driven development even when we're working with SQL Server.