Skip to content

Instantly share code, notes, and snippets.

@darrencauthon
Created March 20, 2015 13:19
Show Gist options
  • Save darrencauthon/caec988d7c928f4ad602 to your computer and use it in GitHub Desktop.
Save darrencauthon/caec988d7c928f4ad602 to your computer and use it in GitHub Desktop.
Chess BDD with SpecFlow
[Binding]
class BoardSteps
{
[When("there is a chess board set up as")]
public void a(Table table)
{
rows = Table.ConvertToSet<BoardRow>();
board = Board.new();
// loop through the rows, load the board
// ...
// now load it for future steps
ScenarioContext.Current.Set(board);
}
public void c(Table table)
{
board = ScenarioContext.Current.Get<Board>();
moves = Table.ConvertToSet<Move>();
// loop through the moves, make them on the board
}
[Then("the (.*) at (.*) should have the following moves")]
public void b(string piece, string location, Table table)
{
expectedMoves = Table.ConvertToSet<Move>();
board = ScenarioContext.Current.Get<Board>();
moves = board.movesFor(location);
moves.CompareToSet(expectedMoves);
}
}
public class BoardRow
{
public string X { get; set; }
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
public string E { get; set; }
public string F { get; set; }
public string G { get; set; }
public string H { get; set; }
}
public class Move
{
public string Start { get; set; }
public string End { get; set; }
}
Feature: King movement
Scenario: A king in its beginning position
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | | | | | |
| 7 | WP | WP | WP | WP | WP | WP | WP | WP |
| 8 | WR | WN | WB | WQ | WK | WB | WN | WR |
Then the WK at E8 should have the following moves
| Start | End |
Scenario: The pawn was moved up
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | | WP | | | |
| 7 | WP | WP | WP | WP | | WP | WP | WP |
| 8 | WR | WN | WB | WQ | WK | WB | WN | WR |
Then the WK at E8 should have the following moves
| Start | End |
| E8 | E7 |
Scenario: Castling when the king hasn't moved
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | | | | | |
| 7 | WP | WP | WP | WP | | WP | WP | WP |
| 8 | WR | WN | WB | WQ | WK | | | WR |
Then the WK at E8 should have the following moves
| Start | End |
| E8 | E7 |
| E8 | G8 |
Scenario: Castling when the king has moved
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | | | | | |
| 7 | WP | WP | WP | WP | | WP | WP | WP |
| 8 | WR | WN | WB | WQ | WK | | | WR |
And the following moves have been made
| Start | End |
| E8 | E7 |
| E7 | E8 |
Then the WK at E8 should have the following moves
| Start | End |
| E8 | E7 |
Feature: Pawn movement
Scenario: A pawn in its beginning position
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | | | | | |
| 7 | | | | WP | | | | |
| 8 | | | | WK | | | | |
Then the WP at D7 should have the following moves
| Start | End |
| D7 | D6 |
| D7 | D5 |
Scenario: A pawn has moved before
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | | | | | | |
| 6 | | | | WP | | | | |
| 7 | | | | | | | | |
| 8 | | | | WK | | | | |
And the following moves have been made
| Start | End |
| D5 | D6 |
Then the WP at D6 should have the following moves
| Start | End |
| D6 | D5 |
Scenario: A pawn has an enemy on the left attacking position
When there is a chess board set up as
| x | a | b | c | d | e | f | g | h |
| 1 | | | | BK | | | | |
| 2 | | | | | | | | |
| 3 | | | | | | | | |
| 4 | | | | | | | | |
| 5 | | | BP | | | | | |
| 6 | | | | WP | | | | |
| 7 | | | | | | | | |
| 8 | | | | WK | | | | |
Then the WP at D6 should have the following moves
| Start | End |
| D6 | D5 |
| D6 | C5 |
@darrencauthon
Copy link
Author

Note: I haven't run this code, this is just a prototype of how using SpecFlow to program a chess game could work.

Note that just a few step definitions are enough to write many detailed Gherkin specs -- so long as those specs are written in a way that can be leveraged by others.

Also note the use of SpecFlow.Assist (built-in to SpecFlow), a namespace that makes building and comparing objects with Gherkin tables easy.

@darrencauthon
Copy link
Author

I love the BDD Cycle as promoted in the RSpec book. The idea is that you use these specs to create the starting point and the potential end points. These tests force you to write just the code you need to make the test run. But what about making it pass? That's when you'd switch to TDD, going red-green in tiny details while the overarching BDD test stays red. Eventually... you'll go red-green on the final unit test that finally makes the feature test go green.

It's sort of like you're an adventurer with a map. You know where you're starting, and you have an idea where you want to end. So you plot those two points on the map... and then you move forward. You don't know what path you'll end up taking, but you know you're not done until you hit the final point.

@darrencauthon
Copy link
Author

One more bit of opinion: I think this is a sweet spot for SpecFlow and testing libraries.

It's not so sweet if you try to run these tests through the UI. In fact, it can become a mess. The tests gets complicated and fragile, and we all know what happens to tests that do that.

I know many people use SpecFlow with other libraries to test through the UI's. They'll talk and talk about how easy it is... and leave it to you fall in to the trap. You'll have invested SO MUCH TIME AND PAIN into those specs, you'll either learn to hate SpecFlow and everything around it, or you'll convince yourself that that time and pain doesn't exist and you'll carry on the lie.

I want my testing to drive my design, and this BDD Cycle is a great way to keep tests driving me at the "forest level" as well as the "tree level." Code testing code, with the intention to keep the UI so stupid-simple that there's little worth testing there.

@erikdietrich
Copy link

Whoah, awesome! The flow (larger scope accpetance tests, micro red-green-refactors to get there) is definitely how I prefer to operate, but you've added an entire visual component to this that had never occurred to me in my initial sizing up of SpecFlow.

Have you implemented a chess game before in this fashion, or are you just operating off the top of your head with this? And, do you mind if I ping you to pick your brain a bit as I'm fleshing out my acceptance test suite for my Chess TDD series?

@darrencauthon
Copy link
Author

@erikdietrich I've never implemented Chess with BDD, but I've used SpecFlow and these sorts of tables in many solutions. In fact, I'm the one who contributed much of code in the the SpecFlow.Assist namespace, as the idea of leveraging the static type definitions of my classes to Gherkin tables came about after experiencing much pain in doing it manually.

So I've never done it with chess, but I have tried to implement my own chess app with TDD back when I was trying to learn TDD. Gherkin tables also happen to be a very close fit to a chess board (I do like to play chess online).

Feel free to contact me if you'd like to talk about what you're building. I'm pretty experienced in SpecFlow, and I bet I could offer some tips. I'm darrencauthon on github, gmail, Skype, Google hangout, twitter, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment