Given-When-Then tests
Tags: testing, bdd, php
Published: 2021-12-27 20:29:53

Consider this test written in the "traditional" format in a test framework of the *[xUnit](https://en.wikipedia.org/wiki/XUnit)* family:

```php
/** @test */
public function calculatesOrderCost(): void
{
    $itemA = Item::withPrice(20);
    $itemB = Item::withPrice(60);
    $shippingCost = ShippingCost::asPercentage(10);

    $order = new Order();
    $order->addItems($itemA, $itemB);
    
    $order->applyShippingCost($shippingCost);

    self::assertEquals(88, $order->totalCost());
}
```

In this post we will analyze the benefits of using the GWT format instead:

```php
/** @test */
public function calculatesOrderCost(): void
{
    $this->givenAnItemWithPrice(20);
    $this->andAnItemWithPrice(60);
    $this->andAShippingCostOfPercent(10);

    $this->whenOrderIsCreated();

    $this->thenTotalOrderCostIs(88);
}
```

## What is GWT

Basically what happened is that we moved the different parts of the test to dedicated methods of any of the GWT types:

* The **G**iven, meaning the state of the system required to execute the code under test.
* The **W**hen, meaning the execution of the code under test.
* The **T**hen, meaning what is the expected output after executing the code.

Since each of these blocks is very likely to have more than one step, each of the blocks can include *And* methods as shown in the example.

GWT comes from BDD (**B**ehavior **D**riven **D**evelopment), an approach to testing coined by [Dan North](https://dannorth.net/introducing-bdd/) with a focus on behaviour, which in turn has lead to the creation of dedicated test frameworks (such as Cucumber, Behat or SpecFlow). In BDD, using GWT is the basis to write the test scenarios, whilst in *xUnit* frameworks is not that common to see this format and certainly more cumbersome to do. They're frameworks were not written with GWT in mind. However, using GWT there provides noticeable benefits as well.

## The benefits

What we achieve with the GWT format is:

**1. Better readability**

Making the tests resemble human readable descriptions of what our application does moves them towards the desirable state where our tests are "living documentation". They reflect what the system does and they will change along with the system when required, making them always up to date documentation.

**2. Reduce the cognitive load**

To get what's going on in the test we can read each step of it without the visual noise produced for the code required to implement it. The example is pretty naive for simplicity purposes, but we can easily imagine `Item` or `Order` would require a lot more parameters to be instantiated while we only care about the ones involving the cost in this case.

**3. Verify the behavior of the system under test instead of its implementation**

Testing the implementation instead of behavior [is not desirable](https://dev.to/xoubaman/writing-better-tests-4apd). It makes our tests and production code less prepared for refactoring and change. Because in GWT we describe what the system does, the test is less prone to fall in the trap of being coupled with the implementation.

It is important to note that GWT does not guarantee any of the above benefits, neither it means they cannot be achieved using another approach or other techniques. It *just* makes them easier to accomplish.

## Trade-offs and considerations

There are few things in software development that have no trade-off included, and BDD and GWT tests are not an exception.

BDD frameworks rely on Gherkin language to define the steps of a test in a separate feature file, and using regular expressions find the matching method implementation. For instance `Given '1' items with price '80' euro*` will be matched with a method `givenItemsWithPrice(int $amount, int $price)`. *xUnit* frameworks are not prepared for this type of pattern matching and the methods in GWT tests do not read as good as the pure BDD counterparts. In our *xUnit* tests we will have just the method implementation, not the more readable feature file.

Another aspect to consider is that when using GWT everything is encapsulated in separate methods and tests tend to store stuff in class properties to use them later on. This means tests may have more properties and state than what we may be used to, with later methods relying on properties set by prior ones. Sometimes this may produce null pointer exceptions and tests that are harder to follow:

```php
/** @test **/
public function shipingToAddress(): void
{
    $this->givenAnAddress(); 
    $this->whenOrderIsShipped();
    $this->thenPackageIsSent();
}

private function givenAnAddress(): void
{
    // We need an address property
    $this->address = new Address('sesame street');
}

private function whenOrderIsShipped(): void
{
    $shippingService = new ShippingService();
    // This method relies on an address previously set, producing a confusing error when it is not
    $shippingService->shipTo($this->address);
}
```

Finally, GWT tests work better when we are testing code involving business rules. When testing classes more related to infrastructure, utilities, data manipulation,... GWT does not work that well and result in cumbersome tests that feel forced:

```php
/** @test **/
public function sanitizeTextGWT(): void
{
    $this->givenATextWithAccents(); 
    $this->whenSanitizingTheText();
    $this->thenAccentsAreRemoved();
}

/** @test **/
public function sanitizeText(): void
{
    // Maybe GWT does not provide much value compared to this
    self::assertEquals('aa', Sanitizer::sanitize('áà'));
}
```

As with every tool, the point is to use the it to solve our problems, not to bend the problems to force them into it. 

## Tips and tricks

Here are some techniques and soft rules that I found give good results when using the GWT approach in *xUnit* tests.

**Do not use And**

Using *and* at the beginning of a method reads pretty well when we write our first tests, but could lead to weird situations when a test needs the steps starting with *and* but none of the ones starting with *Given*, *When* or *Then*:

```php
/** @test */
public function carReactsToAdverseWeather(): void
{
    $this->givenThereIsMist(); // In this test rain is relevant
    $this->andItIsRaining(); // So this reads nice
    $this->whenWeatherIsScanned();
    $this->thenMistLightsAreTurnOn();
    $this->andDriveAssitanceOnWetRoadIsTurnOn();
}

/** @test */
public function carReactsToNotSoAdverseWeather(): void
{
    $this->andItIsRaining(); // We don't care about the mist ant it reads weird 
    $this->whenWeatherIsScanned();
    $this->andDriveAssitanceOnWetRoadIsTurnOn(); // Same here
}
```

Instead, avoid *and* prefix and use always *given*, *when* or *then*. It won't read as good as with *and*, but the benefit is greater than the loss.

```php
/** @test */
public function carReactsToAdverseWeather(): void
{
    $this->givenThereIsMist();
    $this->givenItIsRaining();
    $this->whenWeatherIsScanned();
    $this->thenMistLightsAreTurnOn();
    $this->thenDriveAssitanceOnWetRoadIsTurnOn();
}
```

**Test error cases catching exceptions into a property**

*xUnit* frameworks provide a fancy way of dealing with exceptions when throwing them is the expected behavior. However, it does not fit well in GWT because it would break the test structure or produce "technical sentences" in a otherwise human-readable test. A way of avoiding that is to use a try-catch inside the *When* method saving the exception in a property for a later, more meaningful assertion:

```php
/** @test **/
public function shipingToAddress(): void
{
    $this->givenAnAddress();
    $this->givenClientHasNoFunds();
    $this->whenOrderIsShipped();
    $this->thenOrderFailedDuetoNoPayment();
}

private function whenOrderIsShipped(): void
{
    try {
        $this->shippingService->ship();
    } catch (Exception $ex) {
        $this->exceptionWhenShippingOrder = $ex;
    }
}

private function thenOrderFailedDuetoNoPayment(): void
{
    self::assertInstanceOf(NoPaymentException::class, $this->exceptionWhenShippingOrder)
}
```

**Reduce the number of properties with return values and method parameters**

The body of a GWT is composed of a bunch of methods, one after the other. If we do just that, we will be forced to use lots of properties to hold the values generated in by *Given* methods to use them in the following steps. This may hurt the readability of tests and makes them harder to follow because we cannot notice it unless we go into each of the methods, it is not visible explicitly in the test body.

```php
/** @test */
public function calculatesOrderCost(): void
{
    $this->givenAnItemWithPrice(20); // Requires storing a property for the item
    $this->givenAnItemWithPrice(60); // Another property
    $this->andAShippingCostOfPercent(10); // Another property

    $this->whenOrderIsCreated(); // Will use the previously set properties

    $this->thenTotalOrderCostIs(88);
}
```

But our methods can return stuff and accept parameters, and we can avoid overuse of properties playing with that (which is actually an advantage compared to BDD frameworks).

```php
/** @test */
public function calculatesOrderCost(): void
{
    $oneItem = $this->givenAnItemWithPrice(20);
    $anotherItem = $this->givenAnItemWithPrice(60);
    $shippingCost = $this->andAShippingCostOfPercent(10);

    $this->whenOrderIsCreated($shippingCost, $oneItem, $anotherItem);

    $this->thenTotalOrderCostIs(88);
}
```

## Conclusion

Writing tests in GWT form could result in more valuable tests, because it makes easier to describe the behavior of the system and it kinda enforce us to write our tests *the right way* ™️. Give it a try to check how well it fits with you code style, always taking into account that, as like any other tool, it comes with a number of benefits but also trade-offs.

Hopefully this post has spark some curiosity. Let me know in the comments :)

PS: because I could not figure out something adequate as cover image, I've just randomly used a pic of one of the most mesmerizing animals out there, a Red Panda.