What is a Test Management System (TMS)?
A Test Management System (TMS) is software specifically designed to store, organise, execute, and report on test cases. As QA teams grow beyond a handful of people and products beyond a few features, spreadsheets — the default first-pass tool — fail in predictable ways.
Why spreadsheets fail at scale:
- No versioning: "Which version of the test case was run against which build?" becomes impossible to answer when test cases live in a shared Google Sheet that 10 people edit.
- No execution tracking: A spreadsheet can record pass/fail, but it cannot tell you who executed a test, on which build, at what time, and in which environment without complex manual conventions that nobody follows consistently.
- No traceability: Linking test cases to requirements, requirements to bugs, and bugs to test cases requires a relational data model that a flat spreadsheet cannot provide.
- No reporting: Generating a test execution report from a spreadsheet requires manual calculation. A TMS generates it automatically, filtered by sprint, module, build, or tester.
- No history: When a test case is updated, the old version is gone. A TMS version-controls test cases so you can see what the test looked like when it was last executed against a specific build.
What a TMS provides: version-controlled test cases, execution history linked to builds, traceability from requirements to test cases to bugs, configurable reporting dashboards, and audit trails for regulated environments.
TMS Workflow — Architecture Diagram
How test artefacts flow from requirements through execution to reporting:
In a well-configured TMS workflow, every test case is linked to one or more JIRA stories (the requirements it verifies). Every test execution result is recorded against a specific build and sprint. Every bug discovered during execution is linked to the test case that found it. The traceability matrix is a live view of this web of relationships — not a document you create manually, but a report the TMS generates from the structured data.
JIRA Basics for QA Engineers
JIRA is the dominant issue tracking and project management tool in software development. Understanding its structure is fundamental for QA engineers, even if you use a separate TMS for test cases.
JIRA Issue Types
- Epic: A large body of work spanning multiple sprints. "User Authentication System" is an Epic. QA rarely creates Epics but links work to them.
- Story: A user-facing feature described from the user's perspective. "As a user, I want to reset my password via email." Stories contain acceptance criteria. QA's primary working unit.
- Bug: A defect — behaviour that deviates from specification. QA creates bugs. Key fields: summary, steps to reproduce, expected result, actual result, severity, priority, environment, build version, assignee.
- Task: Technical work without a user-facing component. "Set up staging environment" is a task. QA creates tasks for non-bug work — test data preparation, environment setup, automation framework updates.
- Sub-task: A unit of work within a Story or Bug. "Write test cases for login story" as a sub-task of the login Story keeps testing work visible in the sprint board.
JIRA Project Structure
- Projects: Top-level containers — one project per product or team. QA engineers typically work within the same project as the development team they support.
- Components: Subdivisions of a project — "API", "Frontend", "Mobile", "Authentication". Tag bugs and stories with components to enable component-level reporting and filtering.
- Labels: Free-form tags. QA conventions: "regression", "automation-candidate", "exploratory-finding", "security", "performance". Labels enable cross-component filtering without changing the project structure.
- Sprints: Time-boxed iterations. Stories and bugs are assigned to sprints. Bugs found during Sprint 12 testing should be linked to Sprint 12 even if they are not fixed until Sprint 13.
JQL for QA
JIRA Query Language (JQL) is essential for QA engineers who need to slice data from the project. Useful QA queries:
-- All open bugs assigned to me in the current sprint
assignee = currentUser() AND issuetype = Bug AND status != Done AND sprint in openSprints()
-- All P1 bugs in this project regardless of status
project = MYPROJECT AND issuetype = Bug AND priority = Highest
-- All bugs found in the last sprint that are still open
project = MYPROJECT AND issuetype = Bug AND sprint = "Sprint 12" AND status != Done
-- All stories with no acceptance criteria (requires custom field)
project = MYPROJECT AND issuetype = Story AND "Acceptance Criteria" is EMPTY
-- Bugs assigned to QA for verification
project = MYPROJECT AND issuetype = Bug AND status = "Ready for QA Verification"
Zephyr Scale — JIRA Plugin for Test Management
Zephyr Scale (formerly Zephyr for JIRA, now a SmartBear product) is the most widely used JIRA plugin for test management. It adds a full test management layer directly within JIRA, so test cases, test cycles, and execution results live in the same system as requirements and bugs — no context switching to a separate tool.
Zephyr Scale core concepts:
- Test Cases: Stored in the JIRA project's test repository. Each test case has a unique key (T-123), summary, preconditions, test steps, expected results, priority, and labels.
- Test Cycles: A collection of test cases grouped for execution — typically sprint-based ("Sprint 12 Regression") or feature-based ("User Authentication Test Cycle"). Cycles track execution progress in real time.
- Test Plans: Higher-level containers grouping multiple test cycles. Useful for release-level planning.
- Execution Results: Pass, Fail, Blocked, Not Executed, In Progress. Each result can include a comment, attachment, and a link to the bug if the test failed.
- Traceability: Test cases link to JIRA stories (coverage) and to JIRA bugs (defects found). The coverage report shows which stories have test cases and which do not.
Creating Test Cases in Zephyr Scale
Well-structured test cases in Zephyr follow a consistent format that makes them executable by any QA engineer — not just the person who wrote them:
Test Summary: Concise title in the format "[Feature] — [Scenario]". Example: "Login — Valid credentials redirect to dashboard." Not: "Test login."
Preconditions: The starting state required before executing the test. "User account exists with email: testuser@example.com and password: Test1234. User is not currently logged in. Browser is Chrome 120 in incognito mode."
Test Steps (Step / Action / Expected Result format):
Step 1 | Navigate to https://staging.example.com/login
| The login page loads with email and password fields visible
Step 2 | Enter "testuser@example.com" in the Email field
| The email field contains the entered value
Step 3 | Enter "Test1234" in the Password field
| The password field shows masked characters
Step 4 | Click the "Log In" button
| A loading indicator appears briefly
Step 5 | Wait for page to load
| User is redirected to the dashboard (/dashboard)
| The header displays "Welcome back, Test User"
| The navigation shows all authenticated menu items
Priority: High/Medium/Low — reflecting the business importance of the scenario, not the estimated execution time.
Labels: "regression", "smoke", "authentication", "web" — enables filtering when building test cycles.
Automation status: "Not automated", "Automated", "Cannot be automated". This field drives the automation backlog — filter by "Not automated" + "High priority" to identify automation candidates.
Test Cycles in Zephyr Scale
Test cycles are where test cases are assembled into an executable unit for a specific purpose — a sprint, a release, a regression run, or a UAT session. Cycle workflow:
- Create the cycle: Name it clearly ("Sprint 13 Regression — v2.4.0"), set the sprint association, set the environment (Staging), and set the owner.
- Add test cases: Filter the test repository by label, component, priority, or folder to add the relevant cases. For sprint testing, add all cases linked to the sprint's stories. For regression, add all cases labelled "regression."
- Assign testers: Bulk-assign cases to team members. Zephyr tracks who executed each case.
- Execute: As QA runs each test, update the result in real time: Pass, Fail, Blocked. If Fail, link the bug created in JIRA directly from the execution result.
- Clone cycles: For the next sprint's regression, clone the previous cycle and update it — add new test cases, remove obsolete ones. This preserves history and reduces setup time.
Bulk execution is available for cases that are trivially passing (environment setup validations, for example) — mark multiple cases as passed simultaneously rather than one-by-one to save time.
Xray — Alternative JIRA Plugin
Xray is Zephyr Scale's primary competitor in the JIRA plugin market. It takes a different philosophical approach: where Zephyr uses its own test case format, Xray treats tests as first-class JIRA issue types. This means test cases are searchable in JIRA's main issue search, appear in boards and backlogs alongside stories, and follow the same workflow configurations as other issue types.
Xray's key differentiators:
- Test repository: Tests live in the JIRA project issue list — filterable, searchable, backlog-manageable alongside stories.
- Cucumber integration: Xray provides first-class Cucumber/Gherkin support. Feature files can be exported from Xray, executed by Cucumber, and results imported back automatically. The executable specification model is built in.
- Requirements coverage dashboard: Live view of which requirements have associated tests and what their current execution status is — requirement-level quality visibility, not just test-level.
- Test Plans and Test Executions: Similar to Zephyr's cycles but with tighter JIRA workflow integration — test executions can be linked to JIRA versions for release tracking.
Choose Xray over Zephyr when your team uses BDD/Gherkin heavily, when you want tests to appear in the JIRA backlog alongside stories, or when Cucumber CI integration is a requirement rather than a nice-to-have.
TestRail — Standalone Test Management
TestRail (by Gurock, now part of Idera) is the most popular standalone test management tool — not a JIRA plugin, but a dedicated web application with its own data model and UI. This gives it independence from any project management tool while offering API integrations with JIRA, GitHub, Jenkins, and others.
TestRail's structure:
- Projects: Top-level containers. Each project has its own test repository, runs, milestones, and reports.
- Test Suites: Within a project, suites organise test cases by product area — "Web Application Suite", "API Suite", "Mobile Suite". Suites enable separate regression runs per area.
- Sections: Hierarchical folders within suites — "Authentication", "Authentication / Login", "Authentication / Password Reset". Deep nesting enables granular organisation without bloat.
- Test Cases: Individual test cases with customisable fields — TestRail's custom field system is more flexible than Zephyr's, allowing teams to add project-specific fields (device model, OS version, environment) to every test case.
- Test Runs: The execution unit in TestRail, equivalent to Zephyr's test cycles. Runs are linked to milestones (releases) and can be configured with any subset of test cases from any suite.
- Milestones: Release markers that aggregate runs. The milestone view shows release-level test coverage and execution status across all runs associated with that release.
TestRail API
TestRail's REST API enables automated result pushing from CI pipelines. The most common use case is updating test results directly from pytest or JUnit after an automated test run, so that automated pass/fail results appear alongside manual results in the same TestRail run — giving a unified view of coverage.
The core API endpoint for updating a result:
POST https://yourorg.testrail.io/index.php?/api/v2/add_result_for_case/{run_id}/{case_id}
Headers:
Content-Type: application/json
Authorization: Basic base64(email:api_key)
Body:
{
"status_id": 1, // 1=Passed, 2=Blocked, 4=Retest, 5=Failed
"comment": "Automated test passed in 2.3s",
"elapsed": "2s",
"version": "v2.4.0-build-347"
}
Status IDs: 1 = Passed, 2 = Blocked, 3 = Untested, 4 = Retest, 5 = Failed. Custom statuses can be added in TestRail's administration panel.
pytest Integration with TestRail using trcli
trcli is TestRail's official CLI tool for pushing test results from common formats (JUnit XML, pytest) into TestRail without custom API code:
# Install trcli
pip install trcli
# Run pytest and generate JUnit XML output
pytest tests/ --junitxml=test_results.xml
# Push results to TestRail
trcli -y \
-h https://yourorg.testrail.io \
--project "My Project" \
-u user@example.com \
-p YOUR_API_KEY \
parse_junit \
--suite-id 1 \
--run-id 42 \
-f test_results.xml
For mapping pytest test functions to specific TestRail case IDs, add the case ID as a marker in the test function:
import pytest
@pytest.mark.testrail(case_id=1234)
def test_login_valid_credentials(page):
"""TestRail Case C1234 — Login with valid credentials"""
page.goto("/login")
page.fill("#email", "user@example.com")
page.fill("#password", "Test1234")
page.click("#loginBtn")
assert page.url == "https://staging.example.com/dashboard"
With this setup, every CI pipeline run automatically updates TestRail with the latest automated test results. Manual execution results sit alongside automated results in the same run, giving the team a single source of truth for overall coverage.
Traceability Matrix
A traceability matrix is a document (or report) that maps requirements to test cases to bugs. It answers three questions: Does every requirement have at least one test case? Have all test cases been executed? Are there requirements with open bugs?
Traceability matrix structure:
| Requirement (JIRA Story) | Test Cases (TMS IDs) | Execution Status | Bugs |
|--------------------------|------------------------|------------------|------------|
| AUTH-101: User Login | T-201, T-202, T-203 | All Passed | None |
| AUTH-102: Password Reset | T-204, T-205 | T-204 Failed | BUG-445 |
| AUTH-103: Session Mgmt | T-206, T-207, T-208 | T-208 Blocked | BUG-446 |
| AUTH-104: Account Lock | T-209 | Passed | None |
| AUTH-105: OAuth Login | None | Not tested | N/A |
AUTH-105 with no test cases is the most actionable row in this matrix — it will be flagged in any pre-release review. The traceability matrix makes coverage gaps impossible to ignore.
How to build a traceability matrix: in Zephyr Scale, use the Traceability Report (auto-generated). In TestRail, use the Coverage Report per milestone. In a spreadsheet-based approach, maintain a manually-updated mapping document — but this is a last resort; TMS-generated reports are far more reliable.
Why it matters for audits: regulated industries (finance, healthcare, aerospace, medical devices) require documented evidence that every requirement has been tested. The traceability matrix is the primary artefact auditors examine. An incomplete matrix is a compliance failure.
Test Reporting in a TMS
A TMS generates several report types that provide different views of quality status:
- Pass rate by component: "Authentication: 97% | Checkout: 84% | Search: 91%." Low-pass-rate components indicate where quality attention is needed. Drive the conversation about whether low-pass components are release-ready.
- Bug found by severity per sprint: P1/P2/P3/P4 counts per sprint. Trend analysis: if P1 count is increasing sprint over sprint, there is a systemic quality problem — escalate.
- Sprint test execution status: Live dashboard during the sprint showing executed vs remaining test cases, by tester and by story. Visible in sprint ceremonies — "we have 23 cases remaining; at current velocity, we will finish by Thursday."
- Coverage by module (traceability): Which modules have full requirement-to-test coverage and which have gaps. Input for the go/no-go decision.
- Test cycle history: How the pass rate for the same test suite changes across sprints — regression trend. A regression suite that passes less on each run has a systemic problem: either the product is deteriorating or the tests are becoming outdated.
TMS Tool Comparison
| Dimension | JIRA + Zephyr Scale | JIRA + Xray | TestRail | qTest | Azure Test Plans |
|---|---|---|---|---|---|
| Pricing model | Per-user add-on to JIRA | Per-user add-on to JIRA | Standalone SaaS, per-user | Enterprise pricing | Included in Azure DevOps |
| JIRA integration | Native (same JIRA instance) | Native (same JIRA instance) | Good (API + plugin) | Good (API) | Not applicable |
| REST API quality | Good — SmartBear API | Good — Xray API | Excellent — well documented | Good | Good — Azure DevOps API |
| CI integration | Via API or Zephyr CLI | Native Cucumber sync | trcli, API, JUnit XML | API, JUnit XML | Azure Pipelines native |
| BDD/Gherkin support | Manual feature file link | Excellent — native Cucumber sync | Via API integration | Good | Moderate |
| Learning curve | Medium — JIRA familiarity helps | Medium — steeper for BDD config | Low — clean, intuitive UI | High — feature-rich but complex | Low if team already uses Azure |
Choosing the Right TMS — Decision Guide
The right TMS depends on your specific context. Key decision factors:
- Already using JIRA? Start with Zephyr Scale or Xray. The JIRA integration alone — shared authentication, direct story linking, same bug tracker — delivers significant value over a standalone tool. Xray if you use BDD; Zephyr Scale if you prefer a more traditional test case format.
- Not using JIRA? TestRail is the safest choice for a standalone TMS. Its UI is the most intuitive for QA teams new to test management tooling, its API is the best-documented, and its adoption across the industry is broad enough that most QA engineers have used it before.
- Small team, tight budget? JIRA + Xray or JIRA + Zephyr Scale on a small tier. If budget is truly constrained, consider whether a structured approach with JIRA tasks for test cases (not ideal, but workable) is sufficient until the team grows.
- Enterprise, audit requirements? qTest or TestRail Enterprise provide the advanced reporting, role-based access control, and compliance features regulated industries require. Azure Test Plans is the natural choice if the organisation is on the Microsoft ecosystem (Azure DevOps, Teams, Visual Studio).
- High automation maturity? Prioritise API quality and CI integration. TestRail's trcli and Xray's Cucumber sync are best-in-class for automated result ingestion.
Best Practices for Test Management
- Link every test case to a requirement: Unlinked test cases are orphans — they have no traceability, no audit value, and no way to determine if they are still relevant as requirements change. Every test case should link to at least one JIRA story or requirement document.
- Update results immediately — not at end of sprint: Updating 50 test cases at the end of sprint day 9 from memory is inaccurate and useless for mid-sprint status visibility. Update in real time as tests are executed.
- Use structured test case format (not freeform): "Test login" as a test case summary is not executable by anyone but the author. Use the Action / Expected Result step format. Any QA engineer should be able to execute your test cases on their first day with zero context.
- Run the traceability report before every release: Make this a non-negotiable step in the release checklist. Every requirement must have at least one executed test case with a Pass result. Any requirement without coverage is a go/no-go risk that must be explicitly accepted.
- Review and prune test cases quarterly: Test cases go stale. Features change, user flows change, locators change. A test case repository that grows without pruning becomes a maintenance burden where 30% of cases are outdated or duplicated. Schedule a quarterly review to archive obsolete cases and update changed ones.
- Tag automation status on every case: The gap between "Not automated" and "High priority" test cases is your automation backlog. Make it visible. During sprint planning, advocate for automation stories alongside feature stories — technical debt deferred is technical debt compounded.
Back to Blog