30 Days of Automated Testing:Using PHPUnit【D13】

PHPUnit @ Annotation

WilliamP
3 min readJan 24, 2023

In this article, let’s take a look at several important PHPUnit @ annotations!

The so-called PHPUnit @ annotations refer to the PHP Doc block before the test case function, and are @ annotations provided by PHPUnit for developers to reference. PHPUnit provides about 20+ annotations, and today we will introduce the most commonly used ones to everyone.

Target Function to Test

First, please take a look at the following code, the subsequent @ Annotation examples will be based on this as the test target:

  • app/Services/TestService.php
<?php

namespace App\Services;

use Exception;

class TestService
{
/**
* @throws Exception
*/
public function calculateBmi(float $height, float $weight): float
{
if ($weight <= 0 || $height <= 0) {
throw new Exception('Invalid input!', 1);
}

return $weight / ($height * $height);
}
}

Annotation Introduction

@test

  • Introduction:When the function in the test code has a PHP Doc block that references this Annotation, PHPUnit will treat this function as a test case function and execute it.

Why did some examples in the past two weeks’ articles test functions without using @test, but still work normally? The reason is, in addition to the functions in the test code that reference the @test annotation, PHPUnit will also execute any functions in the test code that start with the word "test", such as testCanCalculateBmi(), as test case functions.

  • Example
<?php

namespace Tests\Feature;

use App\Services\TestService;
use Exception;
use Tests\TestCase;

class ExceptionTest extends TestCase
{
/**
* @test
*/
public function canCalcuateBmi()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$bmiActual = $service->calculateBmi(64.0, 1.6);
$bmiExpected = 64.0/(1.6^2);

$this->assertEquals($bmiExpected, $bmiActual);
}

public function testCanCalcuateBmi()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$bmiActual = $service->calculateBmi(64.0, 1.6);
$bmiExpected = 64.0/(1.6^2);

$this->assertEquals($bmiExpected, $bmiActual);
}

public function canCalcuateBmi2()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$bmiActual = $service->calculateBmi(64.0, 1.6);
$bmiExpected = 64.0/(1.6^2);

$this->assertEquals($bmiExpected, $bmiActual);
}
}

The above code, canCalcuateBmi() and testCanCalcuateBmi() functions will be executed, canCalcuateBmi2() will not be executed.

@depends

  • Introduction:This Annotation, when referenced in the PHP Doc block of a function in the test code, will make PHPUnit ensure that the dependent test case functions have passed validation before executing the current test case function.
  • Example
<?php

namespace Tests\Feature;

use App\Services\TestService;
use Exception;
use Tests\TestCase;

class ExceptionTest extends TestCase
{
public function testCanCalcuateBmi()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$bmiActual = $service->calculateBmi(64.0, 1.6);
$bmiExpected = 64.0/(1.6^2);

$this->assertEquals($bmiExpected, $bmiActual);
}

/**
* @depends testCanCalcuateBmi
*/
public function testCanThrowExceptionWhenInvaliHeight()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$service->calculateBmi(0.0, 1.0);
}
}

The above code, when the testCanCalcuateBmi() test passes, testCanThrowExceptionWhenInvaliHeight() will be executed; when the testCanCalcuateBmi() test fails, testCanThrowExceptionWhenInvaliHeight() will not be executed.

@group

  • Introduction:This Annotation allows grouping multiple test case functions.
  • Example
<?php

namespace Tests\Feature;

use App\Services\TestService;
use Exception;
use Tests\TestCase;

class ExceptionTest extends TestCase
{
/**
* @group BMI
*/
public function testCanCalcuateBmi()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$bmiActual = $service->calculateBmi(64.0, 1.6);
$bmiExpected = 64.0/(1.6^2);

$this->assertEquals($bmiExpected, $bmiActual);
}

/**
* @group BMI
*/
public function testCanThrowExceptionWhenInvaliHeight()
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$service->calculateBmi(0.0, 1.0);
}
}

The above code shows that when we run the command line ./vendor/bin/phpunit --group=BMI, testCanCalcuateBmi() and testCanThrowExceptionWhenInvaliHeight() will both be executed. Additionally, a test case function can be classified into multiple @group.

@testWith

  • Introduction:This Annotation allows us to perform more than one set of data testing for the same test case function.
  • Example
<?php

namespace Tests\Feature;

use App\Services\TestService;
use Exception;
use Tests\TestCase;

class ExceptionTest extends TestCase
{
/**
* @testWith [0.0, 1.0]
* [1.0, 0.0]
* [0.0, 0.0]
*/
public function testCanThrowExceptionWhenInvalidData(float $height, float $weight)
{
$service = app(TestService::class);

$this->expectException(Exception::class);

$service->calculateBmi($height, $weight);
}
}

The above code, testCanThrowExceptionWhenInvalidData() will be executed 3 times:

  • testCanThrowExceptionWhenInvalidData(0.0, 1.0)
  • testCanThrowExceptionWhenInvalidData(1.0, 0.0)
  • testCanThrowExceptionWhenInvalidData(0.0, 0.0)

@dataProvider

  • Introduction:The above code, testCanThrowExceptionWhenInvalidData() will be executed 3 times. Similar to @testWith, but its usage is a bit special, let’s reserve it for the next article for introduction.

The above is today’s introduction, you can all practice more!

Next time, we’ll introduce setUp(), tearDown() and Data Provider, these three special functions!

If you liked this article or found it helpful, feel free to give it some claps and follow the author!

Reference

Articles of This Series

--

--