---
title: PHPUnit reference
description: Reference documentation for Allure PHPUnit integration | How to add steps and additional metadata | Create attachments | Organise tests
---

# Allure PHPUnit reference

These are the attributes and methods that you can use to integrate your PHPUnit tests with Allure Report.

In most cases, Allure PHPUnit provides two different ways to use a feature: the Attributes API and the Runtime API.

- **Attributes API**: add a PHP attribute to a test method or a whole class to add certain data to the test result. When using this approach, the data is guaranteed to be added regardless of how the test itself runs.

- **Runtime API**: use Allure's functions to add certain data to the test result during its execution. This approach allows for constructing the data dynamically.

  Note that it is recommended to call the Allure's functions as close to the beginning of the test as possible. This way, the data will be added even if the test fails early.

## Metadata

Assign a test's [description, links and other metadata](/docs/v2/readability/#description-links-and-other-metadata).

### Title

- `#[DisplayName(string $value)]`
- `Allure::displayName(string $name)`

Set the test's [title](/docs/v2/readability/#title).

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\DisplayName;

final class TestMyWebsite extends TestCase
{
    #[DisplayName('Test Labels')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::displayName('Test Labels');
        // ...
    }
}
```

### Description

- `#[Description(string $value)]`
- `Allure::description(string $description)`

Set the test's [description](/docs/v2/readability/#description). Markdown formatting is allowed. Any HTML formatting, if present, will be stripped for security purposes.

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Description;

final class TestMyWebsite extends TestCase
{
    #[Description('This test attempts to create a label with specified title.')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::description('This test attempts to create a label with specified title.');
        // ...
    }
}
```

### Owner

- `#[Owner(string $value)]`
- `Allure::owner(string $value)`

Set the test's [owner](/docs/v2/readability/#owner).

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Owner;

final class TestMyWebsite extends TestCase
{
    #[Owner('John Doe')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::owner('John Doe');
        // ...
    }
}
```

### Tag

- `#[Tag(string $value)]`
- `Allure::tag(string $value)`

Set the test's [tags](/docs/v2/readability/#tags).

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Tag;

final class TestMyWebsite extends TestCase
{
    #[Tag('UI'), Tag('Labels')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::tag('UI');
        Allure::tag('Labels');
        // ...
    }
}
```

### Severity

- `#[Severity(string $value)]`
- `Allure::severity(Severity $value)`

Set the test's [severity](/docs/v2/readability/#severity).

For the Attributes API, use the constants from the `Severity` class. For the Runtime API, use the static methods on the `Severity` class to construct the values.

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Severity;

final class TestMyWebsite extends TestCase
{
    #[Severity(Severity::CRITICAL)]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;
use Qameta\Allure\Model\Severity;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::severity(Severity::critical());
        // ...
    }
}
```

### Label

- `#[Label(string $name, ?string $value = null)]`
- `Allure::label(string $name, string $value)`

Set an arbitrary [label](/docs/v2/readability/#other-labels) for the test. This is the underlying implementation for a lot of Allure's other APIs.

You may also extend the Attributes API by defining your own attribute that will work as a `#[Label]` with a predefined `$name`.

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Label;

final class TestMyWebsite extends TestCase
{
    #[Label('MyCustomLabel', 'value')]
    public function testLabels()
    {
        // ...
    }
}
```

**Attributes API with a custom attribute:**
```php
use Attribute;
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\AbstractLabel;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final class MyCustomLabel extends AbstractLabel
{
    public function __construct(string $value)
    {
        parent::__construct('MyCustomLabel', $value);
    }
}

final class TestMyWebsite extends TestCase
{
    #[MyCustomLabel('value')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::label('MyCustomLabel', 'value');
        // ...
    }
}
```

### ID

- `#[AllureId(string $value)]`

Set the test's [ID](/docs/v2/readability/#id).

```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\AllureId;

final class TestMyWebsite extends TestCase
{
    #[AllureId('123')]
    public function testLabels()
    {
        // ...
    }
}
```

### Link

- `#[Link(?string $name = null, ?string $url = null, string $type = Link::CUSTOM)]`
- `#[Issue(?string $name = null, ?string $url = null)]`
- `#[TmsLink(?string $name = null, ?string $url = null)]`
- `Allure::link(string $url, ?string $name = null, ?LinkType $type = null)`
- `Allure::issue(string $name, ?string $url = null)`
- `Allure::tms(string $name, ?string $url = null)`

Add a [link](/docs/v2/readability/#links) related to the test.

Based on the `type` (which can be any string), Allure will try to load a corresponding **link pattern** to process the URL, as defined by the [`linkTemplates`](/docs/phpunit-configuration/#linktemplates) configuration option. If no pattern found for the given type, the URL is left unmodified.

The `name` will be used as the link's text. If it is omitted, the unprocessed URL will be used instead.

For convenience, Allure provides shorthand functions with pre-selected link types `Link::ISSUE` and `Link::TMS`.

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Issue;
use Qameta\Allure\Attribute\Link;
use Qameta\Allure\Attribute\TmsLink;

final class TestMyWebsite extends TestCase
{
    #[Link('My Website', 'https://example.com/')]
    #[Issue('UI-123')]
    #[TmsLink('TMS-456')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::link('https://example.com/', 'My Website');
        Allure::issue('UI-123');
        Allure::tms('TMS-456');
        // ...
    }
}
```

## Behavior-based hierarchy

- `#[Epic(string $value)]`
- `#[Feature(string $value)]`
- `#[Story(string $value)]`
- `Allure::epic(string $value)`
- `Allure::feature(string $value)`
- `Allure::story(string $value)`

Assign names of **epics**, **features** or **user stories** for a test, as part of Allure's [behavior-based hierarchy](/docs/v2/navigation/#behavior-based-hierarchy).

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\Epic;
use Qameta\Allure\Attribute\Feature;
use Qameta\Allure\Attribute\Story;

final class TestMyWebsite extends TestCase
{
    #[Epic('Web interface')]
    #[Feature('Essential features')]
    #[Story('Labels')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::epic('Web interface');
        Allure::feature('Essential features');
        Allure::story('Labels');
        // ...
    }
}
```

## Suite-based hierarchy

- `#[ParentSuite(string $value)]`
- `#[Suite(string $value)]`
- `#[SubSuite(string $value)]`
- `Allure::parentSuite(string $value)`
- `Allure::suite(string $value)`
- `Allure::subSuite(string $value)`

Assign the name of the suite, as part of Allure's [suite-based hierarchy](/docs/v2/navigation/#suite-based-hierarchy).

**Attributes API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Attribute\ParentSuite;
use Qameta\Allure\Attribute\SubSuite;
use Qameta\Allure\Attribute\Suite;

final class TestMyWebsite extends TestCase
{
    #[ParentSuite('Web interface')]
    #[Suite('Essential features')]
    #[SubSuite('Labels')]
    public function testLabels()
    {
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::parentSuite('Web interface');
        Allure::suite('Essential features');
        Allure::subSuite('Labels');
        // ...
    }
}
```

## Test steps

- `Allure::runStep(callable $callable, ?string $name = null)`
- `Allure::addStep(string $name, ?Status $status = null)`

Define [test steps](/docs/steps/).

There are three ways of defining a step.

- **Method-based steps**

  Write a test step in a public method and pass it to `runStep()` using the array syntax (i.e., `[$obj, 'method']`). The method should either accept no arguments or accept one argument of the type `StepContextInterface`. During the execution of a step, you can call `runStep()` again to create a sub-step.

  To change a step's title (defaults to just the word “step”), you can:

  - add the [`#[DisplayName]`](#title) attribute to the method,
  - pass the optional `name` argument to `runStep()`,
  - use the `name()` method on the `StepContextInterface` object.

- **Lambda steps**

  Write a test step in a lambda function and pass it to `runStep()`. If the lambda function returns a value, `runStep()` will return it without modification, and it will not affect the report. During the execution of a step, you can call `runStep()` again to create a sub-step.

  To change a step's title (defaults to just the word “step”), you can:

  - add the [`#[DisplayName]`](#title) attribute to the lambda function,
  - pass the optional `name` argument to `runStep()`,
  - use the `name()` method on the `StepContextInterface` object.

- **No-op steps**

  If you call `addStep()`, Allure will add to the report a no-op step. This allows for a log-style reporting within a test or within a larger step. A no-op step finishes immediately after it started and cannot have any sub-steps, or parameters.

  The optional second argument indicates the status that will be shown for the step in the report. Allowed values are: `Status::passed()` (the default), `Status::failed()`, `Status::broken()`, and `Status::skipped()`.

During the execution of a method-based step or a lambda step, it is possible to describe the data the step works with, e.g., if you run it multiple times with different data. To do so, use the `parameter()` method of the `StepContextInterface` object.

**Method-based steps:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;
use Qameta\Allure\Attribute\DisplayName;
use Qameta\Allure\StepContextInterface;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::runStep([$this, 'logIn']);
        Allure::runStep([$this, 'createLabel']);
        Allure::runStep([$this, 'checkThatLabelExists']);
    }

    #[DisplayName('Log in')]
    function logIn(StepContextInterface $context)
    {
        $context->parameter('Email', 'johndoe@example.com');
        $context->parameter('Password', 'qwerty');
        // ...
    }

    #[DisplayName('Create label')]
    function createLabel(StepContextInterface $context)
    {
        $context->parameter('Label name', 'My Label');
        // ...
    }

    #[DisplayName('Check that label exists')]
    function checkThatLabelExists(StepContextInterface $context)
    {
        $context->parameter('Label name', 'My Label');
        // ...
    }
}
```

**Lambda steps:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;
use Qameta\Allure\StepContextInterface;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        Allure::runStep(function (StepContextInterface $context) {
            $context->parameter('Email', 'johndoe@example.com');
            $context->parameter('Password', 'qwerty');
            // ...
        }, 'Log in');

        Allure::runStep(function (StepContextInterface $context) {
            $context->parameter('Label name', 'My Label');
            // ...
        }, 'Create label');

        Allure::runStep(function (StepContextInterface $context) {
            $context->parameter('Label name', 'My Label');
            // ...
        }, 'Check that label exists');
    }
}
```

**No-op steps:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        // ...
        Allure::addStep('Log in');

        // ...
        Allure::addStep('Create label');

        // ...
        Allure::addStep('Check that label exists');
    }
}
```

## Parametrized tests

- `Allure::parameter(string $name, ?string $value, bool $excluded = false, ?ParameterMode $mode = null)`

Allure PHPUnit supports the [parametrized tests](/docs/v2/readability/#parametrized-tests) pattern.

If you pass parameters to your tests via PHPUnit's [data providers](https://docs.phpunit.de/en/10.5/writing-tests-for-phpunit.html#data-providers), the test report will add a numbered pseudo-parameter for each run iteration, e.g., “Data set: #0”, “Data set: #1”, etc. To make the report show the actual parameters and their values, pass them to `Allure::parameter()`.

Note that `Allure::parameter()` to can be used for adding the parameters even to functions outside the PHPUnit's parametrized tests.

If the `excluded` argument is set to true, Allure will not use the parameter when comparing the current test result with previous one in the history.

The `mode` argument affects how the parameter will be displayed in the report. Available options are:

- `null` — the parameter and its value will be shown in a table along with other parameters.
- `ParameterMode::masked()` — the parameter will be shown in the table, but its value will be hidden. Use this mode for passwords, tokens and other sensitive parameters.
- `ParameterMode::hidden()` — the parameter and its value will not be shown in the test report. Note, however, that it is still possible to extract the value from the `allure_results` directory if you publish it.

The parameters can be added not only to whole test results, but also to individual steps within them, see [Test steps](#test-steps) for more details.

**DataProvider:**
```php
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestDataProvider extends TestCase
{
    public static function credentialsProvider()
    {
        return [
            ['johndoe', 'qwerty'],
            ['johndoe@example.com', 'qwerty'],
        ];
    }

    #[DataProvider('credentialsProvider')]
    public function testAuthentication(string $login, string $password)
    {
        Allure::parameter('login', $login);
        Allure::parameter('password', $password);
        // ...
    }
}
```

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testAuthenticationWithUsername()
    {
        Allure::parameter('login', 'johndoe');
        Allure::parameter('password', 'qwerty');
        // ...
    }

    public function testAuthenticationWithEmail()
    {
        Allure::parameter('login', 'johndoe@example.com');
        Allure::parameter('password', 'qwerty');
        // ...
    }
}
```

## Attachments

- `Allure::attachment(string $name, string $content, ?string $type = null, ?string $fileExtension = null)`
- `Allure::attachmentFile(string $name, string $file, ?string $type = null, ?string $fileExtension = null)`

Add an [attachment](/docs/attachments/) to the test result under the given `name`.

Tip:
You can use data produced by any function, not necessarily read from an actual file.

To create an attachment using the Runtime API, call `Allure::attachment()` or `Allure::attachmentFile()` at any point during your test. Pass either the `content` or the `path` from which the data will be read.

To ensure that the reader's web browser will display attachments correctly, it is recommended to specify each attachment's type. To do so, pass the media type of the content as `type` and, optionally, a filename extension as `fileExtension`. The media type affects how the data will be displayed in the test report, while the filename extension is appended to the filename when user wants to save the file.

**Runtime API:**
```php
use PHPUnit\Framework\TestCase;
use Qameta\Allure\Allure;

final class TestMyWebsite extends TestCase
{
    public function testLabels()
    {
        // ...
        Allure::attachment('data.txt', 'This is the file content.', 'text/plain');
        Allure::attachmentFile('data.txt', '/path/to/image.png', 'image/png');
    }
}
```

