C# Unit Testing – Differences Between Given When Then (GWT) and Arrange Act Assert (AAA)

bddctddunit testing

In TDD there is Arrange Act Assert (AAA) syntax:

[Test]
public void Test_ReturnItemForRefund_ReturnsStockOfBlackSweatersAsTwo_WhenOneInStockAndOneIsReturned()
{
    //Arrange
    ShopStock shopStock = new ShopStock();
    Item blackSweater = new Item("ID: 25");
    shopStock.AddStock(blackSweater);
    int expectedResult = 2;
    Item blackSweaterToReturn = new Item("ID: 25");

    //Act
    shopStock.ReturnItemForRefund(blackSweaterToReturn);
    int actualResult = shopStock.GetStock("ID: 25");

    //Assert
    Assert.AreEqual(expectedResult, actualResult);
}

In BDD writing tests uses a similar structure but with Given When Then (GWT) syntax:

    [Given(@"a customer previously bought a black sweater from me")]
    public void GivenACustomerPreviouslyBoughtABlackSweaterFromMe()
    { /* Code goes here */   }

    [Given(@"I currently have three black sweaters left in stock")]
    public void GivenICurrentlyHaveThreeBlackSweatersLeftInStock()
    { /* Code goes here */   }

    [When(@"he returns the sweater for a refund")]
    public void WhenHeReturnsTheSweaterForARefund()
    { /* Code goes here */   }

    [Then(@"I should have four black sweaters in stock")]
    public void ThenIShouldHaveFourBlackSweatersInStock()
    { /* Code goes here */   }

Although they are often considered the same there are differences. A few key ones are:

  1. GWT can be mapped directly to the specification of a feature file in BDD frameworks

  2. GWT is easier for non-developers to understand by encouraging use of plain English, and having a short description of what each part is doing

  3. Given When and Then are keywords in various BDD frameworks such as SpecFlow, and Cucumber

My question is are there any other differences (besides the names) between AAA and GWT? And is there any reason besides the ones specified above that one should be preferred over the other?

Best Answer

I think you listed the differences very well in your question, however I'll add my some of my opinions on how I view the two approaches.

AAA is very useful for me when I'm testing my own code. If I'm working on a project or a library for myself, AAA is the way that I go. It lets me set up whatever I need to execute my test and then just test it. It's quick to setup, and quick to verify that my code is working as I expect.

GWT is useful in business environments, where work that is done by programmers needs to be mapped to business value. Business value is mapped by features, and hopefully features that don't introduce bugs. There are many strategies for mapping features to programming tasks, but one of them is through requirements. In my experience, requirements range from user-level requirements all the way down to small tasks for the user to execute. This is useful because it's easy for the managers to understand how the work the programmer is doing is impacting their customers/users, and therefore why the programmers are adding value to their business

  • User-Level Requirement: Given the warehouse has at least N item(s) in inventory, when a user purchases N item(s), then the warehouse ships N item(s) to the user
  • System-Level Requirement 1: Given the inventory system has N item(s) in inventory, when a request for N item(s) is input into the inventory system then the inventory system decreases the inventory count for that type of item
  • System-Level Requirement 2: Given the payment system has N item(s) in inventory, when a request for N item(s) is input into the payment system then the payment system charges the user for N item(s)
  • ...
  • Programmer-Level Requirement 1: Given 5 sweaters are in inventory, when 3 sweaters are removed from inventory then 2 seaters are left in the inventory
  • ...

This sort of requirement structure allows for a tree-like design where all of the Programmer-Level Requirements map up the tree to the User-Level Requirements. In this way, when a Programmer-Level Requirement fails then you know which User-Level Requirement is affected.

In contrast, an AAA test might look like this. This to me is very programmer-facing and not useful to the business. That's not to say a similar tree structure of requirements couldn't be made from an AAA testing strategy, but nothing in the language of AAA makes it easier to do so.

public void Test_CaseWhereThereAreEnoughSweatersLeft() {
    // Arrange
    // setup Sweater Inventory mock (DB mocks, etc)
    // count = 5
    // when The number of sweaters remaining is request, return count
    // when Inventory is requested to remove N items, then count = count - N

    // Act
    // call the Unit Under Test to remove 3 items from inventory

    // Assert
    // the number of sweaters in the inventory is 2
    // the removal should return indicating a successful removal of items from the inventory
}

public void Test_CaseWhereThereAreNotEnoughSweatersLeft() {
    // Arrange
    // setup Sweater Inventory mock (DB mocks, etc)
    // count = 2
    // when The number of sweaters remaining is request, return count
    // when Inventory is requested to remove N items, then count = count - N

    // Act
    // call the Unit Under Test to remove 3 items from inventory

    // Assert
    // the number of sweaters remaining is still 2
    // the removal should return an error indicating not enough items in the inventory
}
Related Topic