The importance of test consistency
Tags: testing, consistency, laravel
Published: 2018-10-16 21:30:49

---
title: The importance of test consistency
published: true
tags: testing, consistency, laravel
canonical_url: https://davidllop.com/the-importance-of-test-consistency
---

Recently I've discovered the benefits of including feature and unit tests in my applications which helps me find bugs faster.

Today I found a small bug in one of the largest test suite our team has that I was unaware until today.

This test suite contains many tests that work with dates, like hotel bookings or daily price changes. I discovered the 
issue today when the month changed from June to July, let me explain that.

The user needs to be able to modify the price each week but only on certain days, for example every weekend of September. 
So the request receives a `date_begin`, a `date_end` and an array of `weekdays`:

```php
/** @test */
public function only_weekdays_selected_are_created()
{
    // ... Preparing required variables and data on the database
    
    $this->actingAs($user)->post('/prices/create', [
        'price' => 46,
        'date_begin' => Carbon::now()->addMonth()->firstOfMonth(Carbon::MONDAY)->format('Y-m-d'),
        'date_end' => Carbon::now()->addMonth()->firstOfMonth(Carbon::FRIDAY)->format('Y-m-d'),
        'weekdays' => ['wed']
    ]);
    
    $this->assertDatabaseHas('price_days', [
        'day' => Carbon::now()->addMonth()->firstOfMonth(Carbon::WEDNESDAY)->format('Y-m-d'),
        'price' => 46
    ]);
}
```

Have you spotted the bug yet? Well, I'll write it down anyway to force myself to remember it.

The code is inserting the first Wednesday of the first *complete week* into database, which is August 8 2018, and the 
assertion is looking for the first Wednesday, which is August 1 2018. 

After refactoring a bit, the issue is resolved but it forced me to handle this kind of assertions from another point of 
view. Since we are a small team of developers, and all of us needs to write tests on this project, I thought about creating 
some kind of `helper` (it has become an easily used word in programming, don't you think?) to handle that date-stuff for us.

I decided to create a class inside the tests folder and name it `TestCarbon`. This name is nothing close to definitive 
by the way, but is the first thing that came into my mind since it's only for testing and it's related to Carbon.

And this class looks very similar to this:

```php

namespace Tests;

class TestCarbon
{

	public function today()
	{
	    return Carbon::now();
	}
	
	public function firstMondayOfNextMonth()
	{
		return self::today()->addMonth()->firstOfMonth(Carbon::MONDAY);
	}
	
	public function firstWednesdayOfNextMonth()
	{
		return self::firstMondayOfNextMonth()->addDays(2);
	}
	
	public function firstFridayOfNextMonth()
	{
		return self::firstMondayOfNextMonth()->addDays(4);
	}

}
```

Then after refactoring the test, again, it looks like this:

```php
/** @test */
public function only_weekdays_selected_are_created()
{
    // ... Preparing required variables and data on the database
    
    $this->actingAs($user)->post('/prices/create', [
        'price' => 46,
        'date_begin' => TestCarbon::firstMondayOfNextMonth()->format('Y-m-d'),
        'date_end' => TestCarbon::firstFridayOfNextMonth()->format('Y-m-d'),
        'weekdays' => ['wed']
    ]);
    
    $this->assertDatabaseHas('price_days', [
        'day' => TestCarbon::firstWednesdayOfNextMonth()->format('Y-m-d'),
        'price' => 46
    ]);
}
```

I know I know, `firstWednesdayOfNextMonth` is lying (at least in August 2018), but `firstWednesdayInsideFirstFullWeekOfNextMonth` 
seemed too long for me 😅.

I showed the code to the team and they thought it was a good addition to our test suite. They started to use it with their
own tests and adding methods for returning the dates they use the most. It turned out that I had a few tests 
with similar dates but not the same as the three used by `TestCarbon`. But with this class in mind, instead of 
creating new methods for new days, I modified those tests to match this dates and they're still passing. So I was able
to remove some `magic dates`, so to speak, from the code.

Later, I thought about the [Magic string problem](https://en.wikipedia.org/wiki/Magic_string) and I remembered an app in 
which I have a few tests interacting with strings all the time. It was about colors and they were passed 
through the request like `yellow`, `green`, `orange`... This time though, while I was creating this `TestColors` class 
into this test suite, I realised and thought

> why I'm not using this for the main app too?

So I moved that class to a folder freely named `helpers` and refactored both the code in the app and the tests. Now it's 
using this class which contains all the colors I need as constants such as `const YELLOW = 'yellow';`. This allowed
me to remove a lot of magic strings from all the application and everything looks even more consistent now.