threadable / qality-plus
Laravel and PHPUnit integration for publishing automated test results to QAlity Plus.
Requires
- php: ^8.3
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.27
- orchestra/testbench: ^11.1
- phpunit/phpunit: ^12.5|^13.0
This package is auto-updated.
Last update: 2026-08-26 10:33:56 UTC
README
This package records Laravel, PHPUnit, and Pest test results and publishes them to QAlity Plus and Jira from your CI/CD pipeline.
Installation
composer require threadable/qality-plus
If your organization distributes the package through Private Packagist, see Private Packagist setup before running this command.
The package is auto-discovered by Laravel. Publish the configuration only when you need to change a default:
php artisan vendor:publish --tag=qality-config
Setup
Register the PHPUnit extension in phpunit.xml:
<extensions> <bootstrap class="Threadable\QalityPlus\PhpUnit\QalityPlusExtension"> <parameter name="directory" value="storage/qality"/> <parameter name="schema_version" value="1"/> </bootstrap> </extensions>
Add these CI/CD variables:
QALITY_PLUS_API_TOKEN=... QALITY_PLUS_PROJECT_ID=... QALITY_JIRA_BASE_URL=https://example.atlassian.net QALITY_JIRA_EMAIL=ci@example.com QALITY_JIRA_API_TOKEN=... # Jira project containing the QAlity test cases QALITY_JIRA_PROJECT_KEY=QA
The Jira project key is used to find existing QAlity test cases by name. The
default Jira test issue type is QAlity Test; override it with
QALITY_JIRA_TEST_ISSUE_TYPE when necessary.
For alternative authentication, explicit mappings, data providers, or other configuration options, see Advanced configuration.
Map tests to QAlity
To use an existing QAlity test case, put the attribute on the individual test method:
use Threadable\QalityPlus\PhpUnit\QalityTestCase; final class CheckoutTest extends TestCase { #[QalityTestCase('QA-123', requirementIssueKey: 'REQ-42')] public function test_checkout_can_be_completed(): void { // ... } }
To create a missing case with a friendly name, provide only a name:
#[QalityTestCase(name: 'Customer can complete checkout')] public function test_checkout_can_be_completed(): void { // ... }
If only a requirement is supplied, the fully qualified PHPUnit class and method name or Pest description is used as the QAlity test-case name:
#[QalityTestCase(requirementIssueKey: 'NDC-123')] public function test_user_can_reset_their_password(): void { // ... }
When no name is provided, the package uses the fully qualified PHPUnit class and method name, or the Pest test description. If an issue key is already available, the case is treated as existing and its name is not changed.
requirementIssueKey is the source of truth for the Jira issue linked to that
test. It overrides the --work-item value. When it is omitted, the branch
work item is used as the fallback. This applies to both newly created and
already existing QAlity test cases. linkType and linkDirection can override
the configured defaults for an individual test.
CI/CD commands
Run the tests, then create or find the QAlity test cases on feature branches:
php artisan test
php artisan qality:create-test-cases --branch feature/PROJ-123-checkout
When the pipeline does not expose a branch name, provide the Jira work item directly. This skips branch detection and parsing:
php artisan qality:create-test-cases --work-item PROJ-123
On QA, UAT, and production deployments, run the tests and publish their executions:
php artisan test
php artisan qality:publish
Both commands use storage/qality by default. A different result file or
directory can be supplied when needed. Use --dry-run to validate results
without calling QAlity or Jira:
php artisan qality:create-test-cases --branch feature/PROJ-123-checkout --dry-run php artisan qality:publish --dry-run
If an upstream request fails, inspect the Laravel application log (normally
storage/logs/laravel.log) and search for qality-plus. Entries identify
whether the failure occurred in QAlity Plus or Jira, the configured host and
HTTP endpoint, the attempt number, response status, retry decision, and
command stage. Timeout, DNS, and other connection failures are identified
separately; HTTP failures include the returned status code. Request payloads
and authentication tokens are not logged.
The mapping file is optional when QALITY_JIRA_PROJECT_KEY is configured. The
create command first looks for an exact Jira test-case name in the configured
project and creates a new case only when no matching case exists. It also
reconciles the Jira link for cases found through the mapping file or name
lookup. The publish command uses the same lookup when a result has no issue
key. Therefore, a pipeline may discard .qality-test-map.json after each run
when test names are stable and unique; Jira is searched again on the next run.
Name lookups are sent to Jira in batches, then compared against exact issue summaries locally. Only tests with no exact match are sent to the QAlity Plus import endpoint.
If QALITY_JIRA_PROJECT_KEY is not configured and the mapping file is absent,
unmapped tests are sent to QAlity Plus for import on every run and may create
duplicates. Also, --dry-run does not call Jira or QAlity, so its eligible
count does not include name-based lookup results.
Branches are expected to use feature/KEY-123-description,
hotfix/KEY-123-description, or bugfix/KEY-123-description. Use --branch
for CI systems where the branch cannot be detected automatically. Use
--work-item when the work item is already available separately; it takes
precedence over --branch.
Workflow
| Pipeline stage | Command | Result |
|---|---|---|
| Feature branch | qality:create-test-cases |
Finds existing cases or creates missing cases and links each case to its annotated requirement, or to the branch work item when no requirement is annotated |
| QA, UAT, production | qality:publish |
Creates a QAlity Test Cycle and publishes passed, failed, and skipped executions |
Store QAlity and Jira credentials as secured CI/CD variables. The package does not require a particular CI/CD provider.
Newly created QAlity test cases receive the Jira label
threadable-qality-plus by default. Set QALITY_JIRA_CREATED_TEST_LABEL= to
disable the label.
Laravel Boost
This package includes Laravel Boost guidelines and a development skill. In a Laravel application, install Boost as a development dependency and run its installer to make the package guidance available to your AI coding agent:
composer require laravel/boost --dev php artisan boost:install
Contributing
See CONTRIBUTING.md for development and pull-request guidelines. At minimum, install the dependencies and run the full quality check:
composer install composer ci
Please include tests for behavior changes and open an issue before starting large changes.
Security issues should be reported privately as described in SECURITY.md.
License
This package is open-sourced software licensed under the MIT license.