Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

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.


Monday, April 22, 2019

Changing your mindset for starting 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.

Changing the patterns and practices of developers can be a lot like herding cats, especially if they have been following a personal standard for any length of time. It can seem that the harder you try to move people in one direction forcibly, the more resistance you will face. Not only will this resistance affect morale among a team, it will also create, hopefully, figurative claw marks and scratches all over the herder. The key to adoption for any new methodology is to show the overall benefits and have a transition plan. Changing an engrained mindset takes some time, but the results can be monumental and lead to a more productive and effective development team.

Test Driven Development (TDD) is a mindset. What exactly is a mindset? Well, a mindset is a set of beliefs, assumptions, and a perspective a person has about a topic. And, how exactly is TDD a mindset then? TDD is the perspective that it’s more important to understand a problem or requirement before finding or building a solution. This is characterized by writing unit tests before writing any code to satisfy those requirements.

Most developers have the belief that it’s easier to work on the solution first and then while doing so they will begin to understand the complexities of the problem. Is this belief wrong? No, but it’s generally not the most effective. One of the major goals of TDD is to become more effective in writing code and in that effectiveness quality will increase and bugs will decrease. This reason alone is why changing to a TDD mindset will be one of the more beneficial changes a developer can make in their career.

How many times have you or another developer written code weeks, months, or even years ago that maybe was not “clean” or that you even remember ever writing? That code has now become core to the application inadvertently. Today a new developer, or even yourself, makes a change to that code to solve a different requirement. The new code solves the new problem, but now an angry user calls the support desk and says that the feature they relied on for months has stopped working.

The cycle continues of writing code to solve a new problem, break an old feature, fix the old feature, and on and on it goes. This very concern is the solution a TDD mindset will solve. It solves the issue by the codebase always knowing what the desired outcome is for any line of code written because the developer had a clear understanding of the problem before ever writing functional code. Knowing the outcome and having a test written for it helps stop the perpetual cycle of “new code breaks old code” headlines that fill up release notes.

So, the question you’ll be having now is, how do you change to a TDD mindset? It’s all fairly straightforward and the first step is that when reading any requirement or learning of any new features or functionality needed is to ask one simple question: how do I test this? Another way to phrase it is: how do I know that the code I will write is going to satisfy the requirement accurately and completely? Write this question down on a sticky note and put it on however many monitors you have: “HOW DO I TEST THIS?” It cannot be stressed enough how important this question is when changing to a TDD mindset.

The next step for a TDD mindset shift is repetition. Repetition is key here. Asking yourself the above question over and over will help engrain the notion in your mind that understanding the outcome and impact of any requirement is way more important than finding a solution first. You wouldn’t casually build a house upon dirt and then later pour concrete under it, you pour the concrete first and then build the structure from blueprints. The idea is the same in TDD. Have a solid foundation of what needs to be accomplished before ever writing code.

The final step of changing your mindset to a TDD one is probably the most important. This is the step of implementation. It doesn’t do much good to only think about implementing the principles of a TDD mindset without putting them to actual use. It takes real practice to be able to implement anything new and TDD is no different. Without constant practice of getting a requirement, completely understanding it, writing unit tests, and then writing code, it will be very difficult to truly grasp the concept.

Adoption of TDD can be hard to overcome, but by just first thinking about how something can be tested before ever writing code you will have already taken the first step. At the very least start with that question in mind and slowly work your way towards the other steps in TDD. Through time and repetition, you will be well on your way to developing a new mindset that will result in more efficient code with fewer bugs.

Saturday, April 20, 2019

Test-Driven Development on Proof of Concepts

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.


Every developer knows there are times when you just have to play around with some code to see what works and what doesn't. Proof of concept code can be a valuable tool when you're learning something new, trying to figure out a new library, or a plethora of other reasons. Once you've written your proof of concept and it's time to turn into real, quality-driven code, how do you make the transition? This comes up a lot for us so we'll walk you through a few steps that will make sure your proof of concept can seamlessly transition to a test-driven application.

The first thing you'll want to do is take your proof of concept and really look at what it's doing. Think of it like a car: you know it runs, but now you need to really inspect how it runs. You need to look at each individual piece and figure out how they function.

Once you know how the concept code works you'll want to identify any areas that aren't inherently unit-testable. Let's say your proof of concept uses a packaged set of assemblies from a vendor that weren't written to be mocked, spied, etc. You'll want to identify those pieces before you try to start unit testing or it might be bad news later.

Next you'll want to generate some really loose technical requirements. We know, you thought technical requirements were a thing of the past (waterfall, anyone?), but they are sometimes still useful. This is one of those situations. Don't get too specific with them and don't spend a lot of time writing them. Just jot down what's happening in your proof of concept code now so you'll know what tests to write in a few minutes.

At this point you have a working proof of concept, a thorough understanding of how it's working, an awareness of which parts aren't unit-testable, and some really rough technical requirements. You're ready to start your real code now. From this point on you basically get to approach your test-driven process the same way you would if you had not created a proof of concept first. You'll write your tests based on your requirements, write just enough code so the tests can pass, then refactor the code (and the tests) until it all makes you happy. How about an example?

Our feature is a full-fledged card game suite. We ultimately want to have multiple games available where the user can choose which game to play, how much he wants to wager (just for fun!), how many decks of cards to use, and a few other options. The first step is figuring out how to effectively shuffle a deck of cards. No matter what we decide to do with any of the card games, if we can't shuffle the cards we won't create a positive experience for our users. For this sprint (we're agile), one of the stories our team has taken on is developing the card shuffling mechanism that will be used by all of the card games in the future. We decided to write a proof of concept to make sure we have a really solid idea of how we're going to do this (and whether it's even possible) before we start writing our tests and our code.

Now, we want to specify right up front that this isn't necessarily the best way to do this. We're showing how to do it because we know that we don't always get to do things the best way. In those cases we have to accept what we've been given and make the best of it, which is what we're about to do.

Here's what our proof of concept looks like right now:
   1:  public void Shuffle()
   2:  {
   3:      var deck = new Deck { Cards = new List<string>(), IsShuffled = false };
   4:  
   5:      while (deck.Cards.Count < 52)
   6:      {
   7:          var random = new Random();
   8:          var position = random.Next(1, 53);
   9:  
   10:         if (!deck.Cards.Contains(_deck[position]))
   11:         {
   12:             deck.Cards.Add(_deck[position]);
   13:         }
   14:     }
   15: 
   16:     deck.Cards.ForEach(Console.WriteLine);
   17: }

Now we want to go through our process so we can use test-driven development to build the releaseable, reusable shuffle code.
  1. Figure out how everything works
    This is a pretty easy one since our proof of concept code is so simple and small
  2. Identify areas that aren't inherently unit-testable
    We're using the System.Random class, which doesn't have an interface that we can mock so we'll have to do something about that.
    We're not going to cover how to mock this class in this post, but we'll get into it in the future
  3. Generate loose technical requirements
    1. Create an empty deck of cards
    2. Repeat steps 3 and 4 until the new deck is populated with 52 cards
    3. Generate a random number
    4. Put the card represented by the random number into the new deck if it isn't already in there
So now we know what's happening in our proof of concept and we can identify what tests we should write, at least for this part of our shuffler. We generated those technical requirements just so we'd really know what our proof of concept is doing, and therefore what our actual code should do. We don't necessarily need to write a separate unit test for each requirement we generated. In fact, we generally shouldn't write one test per acceptance criterion or requirement.

In this case we can probably get away with writing a unit test called ShuffleShouldRandomizeADeckOfCards. This test will check that after we call the Shuffle method we have a deck of cards that is not in consecutive order. Easy enough.
   1:  [TestMethod]
   2:  public void ShuffleShouldRandomizeADeckOfCards()
   3:  {
   4:      // arrange
   5:      var shuffler = new Shuffler();
   6:   
   7:      // act
   8:      var deck = shuffler.Shuffle();
   9:   
  10:      // assert 
  11:      Assert.AreNotEqual("AH", deck.Cards[0]);
  12:  }

Depending on what our rules are for testing and what our team thinks, this might be good enough. We also might go with something like this:
   1:  [TestMethod]
   2:  public void ShuffleShouldRandomizeADeckOfCards()
   3:  {
   4:      // arrange
   5:      var shuffler = new Shuffler();
   6:   
   7:      // act
   8:      var deck = shuffler.Shuffle();
   9:   
  10:      // assert 
  11:      Assert.AreNotEqual("AH", deck.Cards[0]);
  12:      Assert.AreNotEqual("AD", deck.Cards[13]);
  13:      Assert.AreNotEqual("AC", deck.Cards[26]);
  14:      Assert.AreNotEqual("AS", deck.Cards[39]);
  15:  }

We can make multiple assertions in a single unit test because we're ultimately testing the same thing: whether the deck is shuffled. Remember that at this point our code still looks like this:
   1:  public Deck Shuffle()
   2:  {
   3:      throw new NotImplementedException();
   4:  }

When our test runs, it will fail. Of course it will, we're not doing anything yet! Since we've achieved a red test, we need to make our test turn green.
   1:  public Deck Shuffle()
   2:  {
   3:      var deck = new Deck
   4:      {
   5:          Cards = new List<string>()
   6:      };
   7:   
   8:      deck.Cards.AddRange(new List<string> { "AS", "2S", "3S", "4S", "5S", 
               "6S", "7S", "8S", "9S", "10S", "JS", "QS", "KS" });
   9:      deck.Cards.AddRange(new List<string> { "AC", "2C", "3C", "4C", "5C", 
               "6C", "7C", "8C", "9C", "10C", "JC", "QC", "KC" });
  10:      deck.Cards.AddRange(new List<string> { "AD", "2D", "3D", "4D", "5D", 
               "6D", "7D", "8D", "9D", "10D", "JD", "QD", "KD" });
  11:      deck.Cards.AddRange(new List<string> { "AH", "2H", "3H", "4H", "5H", 
               "6H", "7H", "8H", "9H", "10H", "JH", "QH", "KH" });
  12:   
  13:      return deck;
  14:  }

Now our test passes, but this code isn't shuffling anything. This is probably the hardest part of doing TDD when you already have a proof of concept. We did the proof of concept so we'd know how to do what we want to do, but now we're essentially pretending that we don't know what we want to do. This is tricky, but it's important because by writing this code to turn our test green we've proven that our test isn't very effective. We're going to need some better tests, but remember that our tests shouldn't be more complicated than our code. We'll leave that first test in there for now since we know it passes and we'll add some additional tests, but we need to rename the test since it isn't really checking what the name says. Let's rename it to ShuffleShouldMoveAces. It's not a great name, but it's more descriptive of what's actually going on.

Now we can create another test named ShuffleShouldRandomizeADeckOfCards and do a more extensive check of the randomness of our deck of cards.
   1:  [TestMethod]
   2:  public void ShuffleShouldRandomizeADeckOfCards()
   3:  {
   4:      // arrange
   5:      var unshuffledDeck = new List<string>
   6:      {
   7:          "AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "10H",
   8:          "JH", "QH", "KH", "AD", "2D", "3D", "4D", "5D", "6D", "7D",
   9:          "8D", "9D", "10D", "JD", "QD", "KD", "AC", "2C", "3C", "4C",
  10:          "5C", "6C", "7C", "8C", "9C", "10C", "JC", "QC", "KC", "AS",
  11:          "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "10S", "JS",
  12:          "QS", "KS"
  13:      };
  14:      var shuffler = new Shuffler();
  15:   
  16:      // act
  17:      var deck = shuffler.Shuffle();
  18:   
  19:      // assert
  20:      var numberOfMatches = 0;
  21:      for (var i = 0; i < 52; i++)
  22:      {
  23:          if (unshuffledDeck[i] == deck.Cards[i] ||
  24:              (i > 0 && deck.Cards[i-1] == unshuffledDeck[i]) || 
  25:              (i < 51 && deck.Cards[i + 1] == unshuffledDeck[i]))
  26:          {
  27:              numberOfMatches++;
  28:          }
  29:      }
  30:      Assert.IsTrue(numberOfMatches < 5,
  31:          $"{numberOfMatches} is not less than 5");
  32:  }


What we're doing here is checking whether each card in the shuffled deck is different than the card in the same position as the unshuffled deck, or the position just before it, or the position just after it. Since we know it's theoretically possible for a shuffled card to end up in the same spot in the deck, we've allowed that to happen with up to four cards and still have a passing test. With the code we have right now, this test fails. Now we can implement our proof of concept code to get make it green.
   1:  private readonly Dictionary<int, string> _unshuffledDeck =
   2:              new Dictionary<int, string>
   3:  {
   4:      {1, "AH"}, {2, "2H"}, {3, "3H"}, {4, "4H"}, {5, "5H"},
   5:      { 6, "6H"}, {7, "7H"}, {8, "8H"}, {9, "9H"}, {10, "10H"},
   6:      { 11, "JH"}, {12, "QH"}, {13, "KH"}, {14, "AD"}, {15, "2D"},
   7:      { 16, "3D"}, {17, "4D"}, {18, "5D"}, {19, "6D"}, {20, "7D"},
   8:      { 21, "8D"}, {22, "9D"}, {23, "10D"}, {24, "JD"}, {25, "QD"},
   9:      { 26, "KD"}, {27, "AC"}, {28, "2C"}, {29, "3C"}, {30, "4C"},
  10:      { 31, "5C"}, {32, "6C"}, {33, "7C"}, {34, "8C"}, {35, "9C"},
  11:      { 36, "10C"}, {37, "JC"}, {38, "QC"}, {39, "KC"}, {40, "AS"},
  12:      { 41, "2S"}, {42, "3S"}, {43, "4S"}, {44, "5S"}, {45, "6S"},
  13:      { 46, "7S"}, {47, "8S"}, {48, "9S"}, {49, "10S"}, {50, "JS"},
  14:      { 51, "QS"}, {52, "KS"}
  15:  };
  16:   
  17:  public Deck Shuffle()
  18:  {
  19:      var deck = new Deck
  20:      {
  21:          Cards = new List<string>(),
  22:          IsShuffled = false
  23:      };
  24:   
  25:      while (deck.Cards.Count < 52)
  26:      {
  27:          var random = new Random();
  28:          var position = random.Next(1, 53);
  29:   
  30:          if (!deck.Cards.Contains(_unshuffledDeck[position]))
  31:          {
  32:              deck.Cards.Add(_unshuffledDeck[position]);
  33:          }
  34:      }
  35:   
  36:      return deck;
  37:  }


And there we have it*. We took proof of concept code and ended up doing test-driven development with it.

* There is actually a better way to test the randomness of our Shuffle method, but we're going to cover that in a future blog post about the use of wrappers.

Friday, September 6, 2013

Test-Driven Development

If you're like me, you've heard of Test-Driven Development (TDD) and you're kinda interested, but you haven't put a lot of time into figuring out how to do it.  Maybe you started down the path a long time ago and just got distracted (or bored, or you saw a squirrel).  Well, I recently had good cause to figure it all out and it turns out, it's really easy to learn.

The catch with learning TDD is that (if you're like me) you have to completely change the way you code against requirements.  No big deal, right?  So let me dive in and see if I can explain it simply for you (and remember that my blog is really more about creating an online repository of my own thoughts so I can figure out what I was thinking down the road).

TDD follows a pretty basic process that can be defined as: Red, Green, Refactor.  I prefer to refer to it as Fail, Pass, Clean Up, but that's just because I like to be contrary.  I find TDD easiest when I can identify acceptance criteria of my requirements.  Now, if you do agile you might be mentally protesting (or perhaps verbally, I don't know how unstable you are) that you're not supposed to have firm requirements in agile and that is sorta true, depending on your company's interpretation and implementation of agile.  But that's beside the point.  The point is that regardless of your development methodology you must have acceptance criteria.  Without acceptance criteria how will you know when you're finished coding and how will you know whether you've met your customers' needs?  So we identify acceptance criteria.  For simplicity, let's say our acceptance criteria is "the program displays the entered name".  Once you have your acceptance criteria, you can write your test.  Wait, what?!  Yeah, you create your test based on the acceptance criteria before you write any code at all.  That's why it's called Test-Driven Development.

[TestMethod]
public void ProgramShouldDisplayEnteredName()
{
    var p = new Program();
    var input = "Engineer_Andrew";
    var output = p.DisplayName(input);
    Assert.AreEqual("Engineer_Andrew", output);
}
That test fails, and that's a good thing.  The failed test is our "Red" part.  At this point, my program compiles, but the code doesn't actually do anything.  In fact, the code just throws an exception:

internal object DisplayName(string input)
{
    throw new NotImplementedException();
}

Now that we have a failing test (and we're proud of it) we write just enough code to make it turn "Green", or pass:

internal object DisplayName(string input)
{
    return input;
}

Since this test passes, we can move on to the third step; Refactor.  This is the part where we are going to refactor our code.  If you aren't familiar with that concept, it essentially means you're going to go back and make your code cleaner and/or more efficient and/or more extensible.  It can mean a lot of things, to be honest with you.  In this case it means we're going to do some validation of our input before we return it and we're going to return a string instead of an object:

internal object DisplayName(string input)
{
    if (string.IsNullOrEmpty(input))
    {
        return "Missing Name!";
    }
 
    return input;
}

The only thing left to do is re-run the same test as before and make sure it still passes.  It is also best practice to run all of the tests in your test suite to make sure you didn't inadvertently break something else.  As long as everything passes, you've met your acceptance criteria and you can move on to the next bit of code.

This is a seriously simplified example, but even on a larger scale the method provided here still holds.  Red, Green, Refactor (Fail, Pass, Clean Up).  That's all there is to it.