Reports: Building a custom report plugin (1/3)

As already mentioned in the introduction, reports are developed in PHP and you should have a basic understanding of PHP when you customize or build your own report plugin. You can also contact us if you prefer us to build your customizations and we would be happy to take a look at your requirements.

Please make yourself familiar with the report related terminology and basic structure of report plugins in case you haven't already.

This tutorial walks you through creating a fully working sample custom report plugin. The purpose of this report plugin is to generate reports that display the result distribution for test case types and priorities for a configurable scope (e.g. a milestone or test plan/run). The full source code is also available on GitHub:

"Tests: Property Results" report plugin (TestRail 4.x)
The GitHub repository with the PHP/CSS/JS code for TestRail 4.x

"Tests: Property Results" report plugin (TestRail 3.x)
The GitHub repository with the PHP/CSS/JS code for TestRail 3.x

Please note: The report plugins may differ slightly depending on the TestRail version (e.g. the markup of report forms). Please refer to the GitHub repository and the version that matches your TestRail version for details. This tutorial assumes the latest TestRail version (4.x).

Creating the directory and initial files

The first step is to create the new directory for the report plugin and the initial files.

A good name for report plugins includes the main entity as well as the purpose of the reports. The naming convention is to start with the entity (“tests”) and then add a short description of what the reports are about (e.g. “property_results”).

We then start with the following directory structure:


Recall that about.json is the description file for our report plugin and that report.php contains the PHP-based implementation class. The i18n directory contains the translation file.


The description file is simple and is used to define the label (name), description etc. as well as the translation file:

	"author": "Gurock Software",
	"version": 1,
	"label": "l:reports_tpr_meta_label",
	"summary": "l:reports_tpr_meta_summary",
	"description": "l:reports_tpr_meta_description",
	"group": "l:reports_tpr_meta_group",
	"translations": ["reports_tpr"]


The translation file is set up with the initial strings referenced in the description file and this defines how the report plugin appears in the sidebar of the Reports area in TestRail:

$lang['reports_tpr_meta_label'] = 'Property Results';
$lang['reports_tpr_meta_group'] = 'Samples';
$lang['reports_tpr_meta_summary'] = 'Demonstrates how to develop \
    custom report plugins.';
$lang['reports_tpr_meta_description'] = 'Demonstrates how to develop \
    custom report plugins and serves as a reference for \
    new report plugins.';

The name of the translation file should correlate with the name of the report plugin (tests_property_results → tpr) to minimize the risk of naming conflicts with other report plugins.


Report.php contains the actual implementation of the report plugin and contains all methods to render and validate the report options as well as rendering the actual reports. A typical outline looks as follows (with additional comments that are usually omitted):

class Tests_property_results_report_plugin extends Report_plugin
	public function __construct()
	 * Prepare Form
	 * Expected to initialize the validation for the report-specific
	 * options on the report form and to register all possible report
	 * parameters.
	public function prepare_form($context, $validation)
	 * Validate Form
	 * Expected to validate and return the report parameters (that
	 * were previously registered with prepare_form). Parameters
	 * should be returned as key/value array.
	public function validate_form($context, $input, $validation)
	 * Render Form
	 * Expected to return a string/array with the HTML snippet(s) for
	 * the report-specific part of the report form. If an array is
	 * returned, the following keys are supported:
	 * form:        The HTML snippet with the report options that is
	 *              included as part of the form.
	 * before_form: An HTML snippet that is inserted before the form.
	 *              This should not include any visible HTML code but
	 *              is intended to include HTML/JS/CSS that must not
	 *              be part of the form (such as the HTML for dialogs
	 *              which have their own <form> element).
	 * after_form:	An HTML snippet that is inserted after the form.
	 *              The same conditions apply as for before_form.
	 * Returning a simple string is effectively the same as returning
	 * an array with just the 'form' key.
	public function render_form($context)
	 * Run
	 * Expected to generate the static HTML page for the report. Is
	 * passed the previously configured report parameters (custom
	 * options). Is expected to return an array with the following
	 * keys:
	 * html:        The HTML content as string (should only be used
	 *              for smaller reports or during development).
	 * html_file:   The path of the static HTML file as string. This
	 *              is an alternative and the recommended way to
	 *              return the HTML. This should point to a temporary
	 *              file which is automatically deleted by TestRail
	 *              after 'run' was executed.
	 * resources:   Array of resource files to copy to the output
	 *              directories (optional).
	public function run($context, $options)

TestRail expects a class with the name of the report plugin directory followed by “_report_plugin”. You also need to derive from the base report plugin class named “Report_plugin”.


Except for the constructor, each method is passed a $context parameter that includes details about the context or environment the report plugin is running in. For example, this includes the current project and related options (such as custom field schemes).


TestRail expects a form from every report plugin and the next step is to set up a very basic empty form. To do this, we can ignore prepare_form and validate_form for now and put our focus on render_form:

public function render_form($context)
	$params = array(
		'project' => $context['project']
	return array(
		'form' => $this->render_view(

This basically renders a so called view (called “form”) and returns the result to TestRail. Views are simple PHP files that are responsible for generating static HTML files. They can be passed parameters which are then available in the runtime environment of the view. Views must be placed into the “views” sub-directory of the report plugins and we can leave form.php empty for now:


We can return the form directly as string or use a more complex result in form of an array. We chose the latter in preparation for upcoming changes in this article.

Rendering reports

Reports are supposed to be rendered by “run”. This method is expected to render and return the static HTML along with a list of resources the report references. A minimal implementation looks as follows:

class Tests_property_results_report_plugin extends Report_plugin
	// The resources (files) to copy to the output directory when
	// generating a report.
	private static $_resources = array(
	public function run($context, $options)
		$project = $context['project'];
		// Render the report to a temporary file and return the path
		// to TestRail (including additional resources that need to be
		// copied).
		return array(
			'resources' => self::$_resources,
			'html_file' => $this->render_page(
					'report' => $context['report'],
					'project' => $project

This is usually the most complex method of a report plugin and the place where all the bits come together. The method receives the previously mentioned context and report options from TestRail and usually does the following:

  1. Checks and sets up the scope of the report
  2. Computes the data it wants to render
  3. Passes this data to a view and returns the static HTML

For now, we just render a minimal view (called “index”) and return the result to TestRail (similar to render_form previously). It also returns a list of resources to copy to the report output directory. The view looks as follows:

$min_width = 960;
$header = array(
	'project' => $project,
	'report' => $report,
	'meta' => $report_obj->get_meta(),
	'min_width' => $min_width,
	'css' => array(
		'styles/reset.css' => 'all',
		'styles/view.css' => 'all',
		'styles/print.css' => 'print'
	'js' => array(
$GI->load->view('report_plugins/layout/header', $header);
<p>The report content goes here.</p>
$temp = array();
$temp['report'] = $report;
$temp['meta'] = $report_obj->get_meta();
$temp['show_options'] = true;
$temp['show_report'] = true;
$GI->load->view('report_plugins/layout/footer', $temp);

$GI is the super object in TestRail and is accessible in every view. It provides methods to load sub-views and we use this to reuse TestRail's standard views for report headers and footers.

Develop mode

The next step would be to test our report plugin for the first time. To do this, you would usually go to the Reports tab in TestRail, select the report plugin and press the Add Report button at the bottom of the page. You would then wait until the background task has generated the report and can then view the report. While this workflow is fine for a regular TestRail user, it's a bit cumbersome when you develop, test and debug new report plugins. Fortunately, TestRail has a special develop mode for reports that makes this process a lot simpler.

To enable this mode, please add the following option to TestRail's config.php file:

define('DEPLOY_DEVELOP_REPORT', true);

Once enabled, you should see the following new “Add and View Report” button on the report form:

This button tells TestRail to generate the report immediately and to bypass the usual background processing. You are also automatically redirected to a special page that displays the report once it is available. The best thing about the develop mode is that you can refresh this page anytime and TestRail will then regenerate the report for you.

Running it!

Once you've enabled the develop mode, you can add a new report and should see the following page:

If you see this: well done! If not, you should get a meaningful error message which explains what went wrong but please let us know in case you are stuck.

Next steps

Read on and learn in the next part how to add options to your form and to set up the report scope: