pmd/pmd_userdocs_extending_testing.html

2295 lines
68 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Learn how to use PMD's simple test framework for unit testing rules.">
<meta name="keywords" content="extendinguserdocs, ">
<title>Testing your rules | PMD Source Code Analyzer</title>
<link rel="stylesheet" type="text/css" href="assets/fontawesome-free-5.15.4-web/css/all.min.css">
<link rel="stylesheet" type="text/css" href="assets/bootstrap-4.5.2-dist/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="css/syntax.css">
<link rel="stylesheet" type="text/css" href="css/modern-business.css">
<link rel="stylesheet" type="text/css" href="css/customstyles.css">
<link rel="stylesheet" type="text/css" href="css/theme-green.css">
<link rel="stylesheet" type="text/css" href="css/pmd-customstyles.css">
<link rel="shortcut icon" href="images/logo/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/logo/favicon.ico" type="image/x-icon">
<link rel="alternate" type="application/rss+xml" title="" href="feed.xml">
</head>
<body>
<!-- Content is offset by the height of the topnav bar. -->
<!-- There's already a padding-top rule in modern-business.css, but it apparently doesn't work on Firefox 60 and Chrome 67 -->
<div id="topbar-content-offset">
<!-- Navigation -->
<nav class="navbar navbar-expand-lg fixed-top navbar-dark">
<div class="container topnavlinks">
<a class="navbar-brand fas fa-home fa-lg" href="index.html">&nbsp;<span class="projectTitle"> PMD Source Code Analyzer Project</span></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto mt-2 mt-lg-0"></ul>
<ul class="navbar-nav">
<!-- toggle sidebar button -->
<li class="nav-item"><a id="tg-sb-link" class="nav-link" href="#"><i id="tg-sb-icon" class="fas fa-toggle-on"></i> Nav</a></li>
<!-- entries without drop-downs appear here -->
<li class="nav-item"><a class="nav-link" href="https://github.com/pmd/pmd/releases/latest" target="_blank">Download</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/pmd/pmd" target="_blank">Fork us on github</a></li>
<!-- entries with drop-downs appear here -->
<!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="search..." id="search-input">
<ul id="results-container"></ul>
</form>
</div>
</div>
</nav>
<!-- Page Content -->
<div class="container-toc-wrapper">
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
<!-- Sidebar Column -->
<div class="col-md-3" id="tg-sb-sidebar">
<ul id="mysidebar" class="nav">
<li class="sidebarTitle">PMD 7.8.0-SNAPSHOT</li>
<div class="sidebarTitleDate">Release date: 29-November-2024</div>
<li>
<a href="#">About</a>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="pmd_release_notes.html">Release notes</a></li>
<li><a href="pmd_release_notes_pmd7.html">Release notes (PMD 7)</a></li>
<li><a href="pmd_about_help.html">Getting help</a></li>
<li><a href="pmd_about_release_policies.html">Release policies</a></li>
<li><a href="pmd_about_support_lifecycle.html">Support lifecycle</a></li>
</ul>
</li>
<li>
<a href="#">User Documentation</a>
<ul>
<li><a href="pmd_userdocs_migrating_to_pmd7.html">Migration Guide for PMD 7</a></li>
<li><a href="pmd_userdocs_installation.html">Installation and basic CLI usage</a></li>
<li><a href="pmd_userdocs_making_rulesets.html">Making rulesets</a></li>
<li><a href="pmd_userdocs_configuring_rules.html">Configuring rules</a></li>
<li><a href="pmd_userdocs_best_practices.html">Best practices</a></li>
<li><a href="pmd_userdocs_suppressing_warnings.html">Suppressing warnings</a></li>
<li><a href="pmd_userdocs_incremental_analysis.html">Incremental analysis</a></li>
<li><a href="pmd_userdocs_cli_reference.html">PMD CLI reference</a></li>
<li><a href="pmd_userdocs_report_formats.html">PMD Report formats</a></li>
<li><a href="pmd_userdocs_3rdpartyrulesets.html">3rd party rulesets</a></li>
<li class="subfolders">
<a href="#">CPD reference</a>
<ul>
<li><a href="pmd_userdocs_cpd.html">Copy-paste detection</a></li>
<li><a href="pmd_userdocs_cpd_report_formats.html">CPD Report formats</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Extending PMD</a>
<ul>
<li><a href="pmd_userdocs_extending_writing_rules_intro.html">Introduction to writing rules</a></li>
<li><a href="pmd_userdocs_extending_your_first_rule.html">Your first rule</a></li>
<li><a href="pmd_userdocs_extending_writing_xpath_rules.html">XPath rules</a></li>
<li><a href="pmd_userdocs_extending_writing_java_rules.html">Java rules</a></li>
<li><a href="pmd_userdocs_extending_designer_reference.html">Rule designer reference</a></li>
<li><a href="pmd_userdocs_extending_defining_properties.html">Defining rule properties</a></li>
<li><a href="pmd_userdocs_extending_rule_guidelines.html">Rule guidelines</a></li>
<li class="active"><a href="pmd_userdocs_extending_testing.html">Testing your rules</a></li>
<li><a href="pmd_userdocs_extending_ast_dump.html">Creating (XML) dump of the AST</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Tools / Integrations</a>
<ul>
<li><a href="pmd_userdocs_tools_maven.html">Maven PMD Plugin</a></li>
<li><a href="pmd_userdocs_tools_gradle.html">Gradle</a></li>
<li><a href="pmd_userdocs_tools_ant.html">Ant</a></li>
<li><a href="pmd_userdocs_tools_java_api.html">PMD Java API</a></li>
<li><a href="pmd_userdocs_tools_bld.html">bld PMD Extension</a></li>
<li><a href="pmd_userdocs_tools_ci.html">CI integrations</a></li>
<li><a href="pmd_userdocs_tools.html">Other Tools / Integrations</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Rule Reference</a>
<ul>
<li class="subfolders">
<a href="#">Apex Rules</a>
<ul>
<li><a href="pmd_rules_apex.html">Index</a></li>
<li><a href="pmd_rules_apex_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_apex_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_apex_design.html">Design</a></li>
<li><a href="pmd_rules_apex_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_apex_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_apex_performance.html">Performance</a></li>
<li><a href="pmd_rules_apex_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">HTML Rules</a>
<ul>
<li><a href="pmd_rules_html.html">Index</a></li>
<li><a href="pmd_rules_html_bestpractices.html">Best Practices</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Rules</a>
<ul>
<li><a href="pmd_rules_java.html">Index</a></li>
<li><a href="pmd_rules_java_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_java_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_java_design.html">Design</a></li>
<li><a href="pmd_rules_java_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_java_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_java_multithreading.html">Multithreading</a></li>
<li><a href="pmd_rules_java_performance.html">Performance</a></li>
<li><a href="pmd_rules_java_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Server Pages Rules</a>
<ul>
<li><a href="pmd_rules_jsp.html">Index</a></li>
<li><a href="pmd_rules_jsp_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_jsp_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_jsp_design.html">Design</a></li>
<li><a href="pmd_rules_jsp_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_jsp_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">JavaScript Rules</a>
<ul>
<li><a href="pmd_rules_ecmascript.html">Index</a></li>
<li><a href="pmd_rules_ecmascript_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_ecmascript_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_ecmascript_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_ecmascript_performance.html">Performance</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Kotlin Rules</a>
<ul>
<li><a href="pmd_rules_kotlin.html">Index</a></li>
<li><a href="pmd_rules_kotlin_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_kotlin_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Maven POM Rules</a>
<ul>
<li><a href="pmd_rules_pom.html">Index</a></li>
<li><a href="pmd_rules_pom_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Modelica Rules</a>
<ul>
<li><a href="pmd_rules_modelica.html">Index</a></li>
<li><a href="pmd_rules_modelica_bestpractices.html">Best Practices</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">PLSQL Rules</a>
<ul>
<li><a href="pmd_rules_plsql.html">Index</a></li>
<li><a href="pmd_rules_plsql_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_plsql_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_plsql_design.html">Design</a></li>
<li><a href="pmd_rules_plsql_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Salesforce Visualforce Rules</a>
<ul>
<li><a href="pmd_rules_visualforce.html">Index</a></li>
<li><a href="pmd_rules_visualforce_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Scala Rules</a>
<ul>
<li><a href="pmd_rules_scala.html">Index</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Swift Rules</a>
<ul>
<li><a href="pmd_rules_swift.html">Index</a></li>
<li><a href="pmd_rules_swift_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_swift_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Velocity Template Language (VTL) Rules</a>
<ul>
<li><a href="pmd_rules_velocity.html">Index</a></li>
<li><a href="pmd_rules_velocity_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_velocity_design.html">Design</a></li>
<li><a href="pmd_rules_velocity_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">WSDL Rules</a>
<ul>
<li><a href="pmd_rules_wsdl.html">Index</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XML Rules</a>
<ul>
<li><a href="pmd_rules_xml.html">Index</a></li>
<li><a href="pmd_rules_xml_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_xml_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XSL Rules</a>
<ul>
<li><a href="pmd_rules_xsl.html">Index</a></li>
<li><a href="pmd_rules_xsl_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_xsl_performance.html">Performance</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Language-Specific Documentation</a>
<ul>
<li><a href="pmd_languages_index.html">Overview</a></li>
<li><a href="pmd_languages_configuration.html">Language configuration</a></li>
<li><a href="pmd_languages_apex.html">Apex</a></li>
<li><a href="pmd_languages_cpp.html">C/C++</a></li>
<li><a href="pmd_languages_cs.html">C#</a></li>
<li><a href="pmd_languages_coco.html">Coco</a></li>
<li><a href="pmd_languages_dart.html">Dart</a></li>
<li><a href="pmd_languages_fortran.html">Fortran</a></li>
<li><a href="pmd_languages_gherkin.html">Gherkin</a></li>
<li><a href="pmd_languages_go.html">Go</a></li>
<li><a href="pmd_languages_html.html">HTML</a></li>
<li><a href="pmd_languages_java.html">Java</a></li>
<li><a href="pmd_languages_js_ts.html">JavaScript / TypeScript</a></li>
<li><a href="pmd_languages_jsp.html">JSP</a></li>
<li><a href="pmd_languages_julia.html">Julia</a></li>
<li><a href="pmd_languages_kotlin.html">Kotlin</a></li>
<li><a href="pmd_languages_lua.html">Lua</a></li>
<li><a href="pmd_languages_matlab.html">Matlab</a></li>
<li><a href="pmd_languages_modelica.html">Modelica</a></li>
<li><a href="pmd_languages_objectivec.html">Objective-C</a></li>
<li><a href="pmd_languages_perl.html">Perl</a></li>
<li><a href="pmd_languages_php.html">PHP</a></li>
<li><a href="pmd_languages_plsql.html">PLSQL</a></li>
<li><a href="pmd_languages_python.html">Python</a></li>
<li><a href="pmd_languages_ruby.html">Ruby</a></li>
<li><a href="pmd_languages_scala.html">Scala</a></li>
<li><a href="pmd_languages_swift.html">Swift</a></li>
<li><a href="pmd_languages_tsql.html">T-SQL</a></li>
<li><a href="pmd_languages_visualforce.html">Visualforce</a></li>
<li><a href="pmd_languages_velocity.html">Velocity Template Language (VTL)</a></li>
<li><a href="pmd_languages_xml.html">XML and XML dialects</a></li>
</ul>
</li>
<li>
<a href="#">Developer Documentation</a>
<ul>
<li><a href="pmd_devdocs_development.html">Developer resources</a></li>
<li><a href="pmd_devdocs_building.html">Building PMD from source</a></li>
<li><a href="https://github.com/pmd/pmd/blob/main/CONTRIBUTING.md" target="_blank">Contributing</a></li>
<li><a href="pmd_devdocs_writing_documentation.html">Writing documentation</a></li>
<li><a href="pmd_devdocs_roadmap.html">Roadmap</a></li>
<li><a href="pmd_devdocs_how_pmd_works.html">How PMD works</a></li>
<li><a href="pmd_devdocs_pmdtester.html">Pmdtester</a></li>
<li><a href="pmd_devdocs_rule_deprecation_policy.html">Rule Deprecation Policy</a></li>
<li class="subfolders">
<a href="#">Major contributions</a>
<ul>
<li><a href="pmd_devdocs_major_rule_guidelines.html">Rule Guidelines</a></li>
<li><a href="pmd_devdocs_major_adding_new_language_javacc.html">Adding a new language (JavaCC)</a></li>
<li><a href="pmd_devdocs_major_adding_new_language_antlr.html">Adding a new language (ANTLR)</a></li>
<li><a href="pmd_devdocs_major_adding_new_cpd_language.html">Adding a new CPD language</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Experimental features</a>
<ul>
<li><a href="tag_experimental.html">List of experimental Features</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Project documentation</a>
<ul>
<li class="subfolders">
<a href="#">Trivia about PMD</a>
<ul>
<li><a href="pmd_projectdocs_trivia_news.html">PMD in the press</a></li>
<li><a href="pmd_projectdocs_trivia_products.html">Products & books related to PMD</a></li>
<li><a href="pmd_projectdocs_trivia_similarprojects.html">Similar projects</a></li>
<li><a href="pmd_projectdocs_trivia_meaning.html">What does 'PMD' mean?</a></li>
</ul>
</li>
<li><a href="pmd_projectdocs_logo.html">Logo</a></li>
<li><a href="pmd_projectdocs_faq.html">FAQ</a></li>
<li><a href="license.html">License</a></li>
<li><a href="pmd_projectdocs_credits.html">Credits</a></li>
<li><a href="pmd_release_notes_old.html">Old release notes</a></li>
<li><a href="pmd_projectdocs_decisions.html">Decisions</a></li>
<li class="subfolders">
<a href="#">Project management</a>
<ul>
<li><a href="pmd_projectdocs_committers_infrastructure.html">Infrastructure</a></li>
<li><a href="pmd_projectdocs_committers_releasing.html">Release process</a></li>
<li><a href="pmd_projectdocs_committers_merging_pull_requests.html">Merging pull requests</a></li>
<li><a href="pmd_projectdocs_committers_main_landing_page.html">Main Landing page</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<!-- Content Column -->
<div class="col-md-9" id="tg-sb-content">
<header>
<div class="row">
<div class="col-lg-12">
<a href="./" role="button"
><i class="fa fa-home fa-lg"></i
></a>
» Testing your rules
<a
target="_blank"
href="https://github.com/pmd/pmd/blob/main/docs/pages/pmd/userdocs/extending/testing.md"
class="float-right"
role="button"
><i class="fab fa-github fa-lg"></i> Edit on GitHub</a
>
</div>
</div>
<hr />
</header>
<div class="post-header">
<h1 class="post-title-main">Testing your rules</h1>
</div>
<div class="post-content" data-github-edit-url="https://github.com/pmd/pmd/blob/main/docs/pages/pmd/userdocs/extending/testing.md">
<div class="summary">Learn how to use PMD's simple test framework for unit testing rules.</div>
<details id="inline-toc-details">
<summary>Table of Contents</summary>
<div id="inline-toc"><!-- empty, move TOC here when screen size too small --></div>
</details>
<h2 id="introduction">Introduction</h2>
<p>Good rules have tests. At least a positive test case - a code example, that triggers the rule and reports
a violation - and a negative test case - a code example, that doesnt trigger the rule - should be created.
Of course, the more tests, the better the rule is verified. If the rule is more complex or defines properties,
with which the behavior can be modified, then these different cases can also be tested.</p>
<p>And if there is a bug fix for a rule, be it a false positive or a false negative case, it should be accompanied
by an additional test case, so that the bug is not accidentally reintroduced later on.</p>
<h2 id="how-it-works">How it works</h2>
<p>PMDs built-in rules are organized in rulesets, where all rules belonging to the same category are placed
in a single ruleset, such as “category/java/bestpractices.xml”.
Each category-ruleset has a single abstract base test class, from which the individual test classes inherit.
We have one test class per rule, which executes all test cases for a single rule. The actual test cases are
stored in separate XML files, for each rule a separate file is used.</p>
<p>All the test classes inherit from <a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/PmdRuleTst.html#"><code>PmdRuleTst</code></a>,
which provides the seamless integration with JUnit5. This base class determines the language, the category name
and the rule name from the concrete test class. It then searches the test code on its own.
E.g. the individual rule test class
<code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.java.rule.bestpractices.AbstractClassWithoutAbstractMethodTest</code> tests the
rule with the name “AbstractClassWithoutAbstractMethod”, which is in the category “bestpractices” for the
language “java”.</p>
<p>The test code (see below <a href="#test-xml-reference">Test XML Reference</a>) describes the test case completely with
the expected behavior like number of expected rule violations, where the violations are expected, and so on.</p>
<p>When you are running the test class in your IDE (e.g. Eclipse or IntelliJ IDEA) you can also select a single
test case and just execute this one.</p>
<h2 id="where-to-place-the-test-code">Where to place the test code</h2>
<p>The <a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/PmdRuleTst.html#"><code>PmdRuleTst</code></a> class searches the XML file, that describes the test cases
for a certain rule using the following convention:</p>
<p>The XML file is a test resource, so it is searched in the tree under <code class="language-plaintext highlighter-rouge">src/test/resources</code>.</p>
<p>The sub package <code class="language-plaintext highlighter-rouge">xml</code> of the test classs package should contain a file with the same name as the rules name
which is under test.</p>
<p>For example, to test the rule “AbstractClassWithoutAbstractMethod”, the fully qualified test class is:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.sourceforge.pmd.lang.java.rule.bestpractices.AbstractClassWithoutAbstractMethodTest
</code></pre></div></div>
<p>The test code for the rule can be found in the file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AbstractClassWithoutAbstractMethod.xml
</code></pre></div></div>
<p>In general, the class name and file name pattern for the test class and data is this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>net.sourceforge.pmd.lang.&lt;Language Id&gt;.rule.&lt;Category Name&gt;.&lt;Rule Name&gt;Test
src/test/resources/net/sourceforge/pmd/lang/&lt;Language Id&gt;/rule/&lt;Category Name&gt;/xml/&lt;Rule Name&gt;.xml
</code></pre></div></div>
<p>Note: Language Id is the id defined by the language module, see <a href="https://docs.pmd-code.org/apidocs/pmd-core/7.8.0-SNAPSHOT/net/sourceforge/pmd/lang/Language.html#getId()"><code>getId</code></a>.</p>
<div class="alert alert-success" role="alert"><i class="fas fa-check-square-o"></i> <b>Tip:</b> This convention allows you to quickly find the test cases for a given rule:
Just search in the project for a file <code class="language-plaintext highlighter-rouge">&lt;Rule Name&gt;.xml</code>. Search for a class <code class="language-plaintext highlighter-rouge">&lt;Rule Name&gt;Test</code> to find the
unit test class for the given rule. And if the rule is a Java-based rule, the search for <code class="language-plaintext highlighter-rouge">&lt;Rule Name&gt;Rule</code>
finds the rule implementation class.</div>
<div class="alert alert-info" role="alert"><i class="fas fa-info-circle"></i> <b>Note:</b> If you want to use the test framework with a different package structure,
see <a href="#using-the-test-framework-externally">Using the test framework externally</a>.</div>
<h2 id="simple-example">Simple example</h2>
<h3 id="test-class-abstractclasswithoutabstractmethodtest">Test Class: AbstractClassWithoutAbstractMethodTest</h3>
<p>This class inherits from <a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/PmdRuleTst.html#"><code>PmdRuleTst</code></a> and is located in the package “bestpractices”,
since the rule belongs to the category “Best Practices”:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">net.sourceforge.pmd.lang.java.rule.bestpractices</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">net.sourceforge.pmd.test.PmdRuleTst</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">AbstractClassWithoutAbstractMethodTest</span> <span class="kd">extends</span> <span class="nc">PmdRuleTst</span> <span class="o">{</span>
<span class="c1">// no additional unit tests</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="alert alert-info" role="alert"><i class="fas fa-info-circle"></i> <b>Note:</b> You can also add additionally standard JUnit test methods annotated with <code class="language-plaintext highlighter-rouge">@Test</code> to
this test class.</div>
<h3 id="test-data-avoidbranchingstatementaslastinloopxml">Test Data: AvoidBranchingStatementAsLastInLoop.xml</h3>
<p>This is a stripped down example which just contains two test cases.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;test-data</span>
<span class="na">xmlns=</span><span class="s">"http://pmd.sourceforge.net/rule-tests"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://pmd.sourceforge.net/rule-tests https://pmd.sourceforge.io/rule-tests_1_0_0.xsd"</span><span class="nt">&gt;</span>
<span class="nt">&lt;test-code&gt;</span>
<span class="nt">&lt;description&gt;</span>concrete class<span class="nt">&lt;/description&gt;</span>
<span class="nt">&lt;expected-problems&gt;</span>0<span class="nt">&lt;/expected-problems&gt;</span>
<span class="nt">&lt;code&gt;</span><span class="cp">&lt;![CDATA[
public class Foo {}
]]&gt;</span><span class="nt">&lt;/code&gt;</span>
<span class="nt">&lt;/test-code&gt;</span>
<span class="nt">&lt;test-code&gt;</span>
<span class="nt">&lt;description&gt;</span>failure case<span class="nt">&lt;/description&gt;</span>
<span class="nt">&lt;expected-problems&gt;</span>1<span class="nt">&lt;/expected-problems&gt;</span>
<span class="nt">&lt;expected-linenumbers&gt;</span>1<span class="nt">&lt;/expected-linenumbers&gt;</span>
<span class="nt">&lt;code&gt;</span><span class="cp">&lt;![CDATA[
public abstract class Foo {}
]]&gt;</span><span class="nt">&lt;/code&gt;</span>
<span class="nt">&lt;/test-code&gt;</span>
<span class="nt">&lt;/test-data&gt;</span>
</code></pre></div></div>
<p>Each test case is in an own <code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> element. The first defines 0 expected problems, means this code doesnt
trigger the rule. The second test case expects 1 problem. Since the rule violations also report the exact AST node,
you can verify the line number, too.</p>
<h2 id="test-xml-reference">Test XML Reference</h2>
<p>The root element is <code class="language-plaintext highlighter-rouge">&lt;test-data&gt;</code>. It can contain one or more <code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;code-fragment&gt;</code> elements.
Each <code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> element defines a single test case. <code class="language-plaintext highlighter-rouge">&lt;code-fragment&gt;</code> elements are used to share code snippets
between different test cases.</p>
<div class="alert alert-info" role="alert"><i class="fas fa-info-circle"></i> <b>Note:</b> The XML schema is available at <a href="https://github.com/pmd/pmd/blob/main/pmd-test-schema/src/main/resources/net/sourceforge/pmd/test/schema/rule-tests_1_0_0.xsd">rule-tests.xsd</a>.</div>
<h3 id="test-code-attributes"><code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> attributes</h3>
<p>The <code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> elements understands the following optional attributes:</p>
<ul>
<li>
<p><strong>disabled</strong>: By default, its <code class="language-plaintext highlighter-rouge">false</code>. Set it to <code class="language-plaintext highlighter-rouge">true</code>, to ignore and skip a test case.</p>
</li>
<li>
<p><strong>focused</strong>: By default, its <code class="language-plaintext highlighter-rouge">false</code>. Set it to <code class="language-plaintext highlighter-rouge">true</code>, to ignore all other test cases. This is useful while
debugging a rule and you want to focus only on one specific case.</p>
</li>
</ul>
<h3 id="test-code-children"><code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code> children</h3>
<ul>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;description&gt;</code></strong>: Short description of the test case. This will be the JUnit test name in the report.
If applicable, this description should contain a reference to the bug number, this test case reproduces.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;rule-property&gt;</code></strong>: Optional rule properties, if the rule is configurable. Just add multiple elements, to
set multiple properties for one test case. For an example, see below.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;expected-problems&gt;</code></strong>: The raw number of expected rule violations, that this rule is expected to report.
For false-positive test cases, this is always “0”. For false-negative test cases, it can be any positive number.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;expected-linenumbers&gt;</code></strong>: Optional element. Its a comma separated list of line numbers.
If there are rule violations reported, then this allows you to
assert the line numbers. Useful if multiple violations should be detected and to be sure that
false positives and negatives dont erase each other.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;expected-messages&gt;</code></strong>: Optional element, with <code class="language-plaintext highlighter-rouge">&lt;message&gt;</code> elements as children.
Can be used to validate the correct error message, e.g. if the error message references a variable name.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;code&gt;</code></strong>: Either the <code class="language-plaintext highlighter-rouge">&lt;code&gt;</code> element or the <code class="language-plaintext highlighter-rouge">&lt;code-ref&gt;</code> element is required. It provides the actual code
snippet on which the rule is executed. The code itself is usually wrapped in a “CDATA” section, so that no
further XML escapes (entity references such as &amp;lt;) are necessary.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;code-ref id=...&gt;</code></strong>: Alternative to <code class="language-plaintext highlighter-rouge">&lt;code&gt;</code>. References a <code class="language-plaintext highlighter-rouge">&lt;code-fragment&gt;</code> defined earlier in the file.
This allows you to share the same code snippet with several test cases. The attribute <code class="language-plaintext highlighter-rouge">id</code> must match the
id of the references code fragment.</p>
</li>
<li>
<p><strong><code class="language-plaintext highlighter-rouge">&lt;source-type&gt;</code></strong>: Optional element that specifies a specific language version. This can be used
to select a specific parser version for parsing the code snippet. If not given, the default version of
the rules language is used. This element can almost always be omitted.</p>
</li>
</ul>
<h3 id="code-fragment"><code class="language-plaintext highlighter-rouge">&lt;code-fragment&gt;</code></h3>
<p>The code fragment has just one required attribute: <strong>id</strong>. This is used to reference it via a <code class="language-plaintext highlighter-rouge">&lt;code-ref&gt;</code> element
inside a <code class="language-plaintext highlighter-rouge">&lt;test-code&gt;</code>. Similar like the <code class="language-plaintext highlighter-rouge">&lt;code&gt;</code> element, the content of <code class="language-plaintext highlighter-rouge">&lt;code-fragment&gt;</code> is usually wrapped
in a “CDATA” section, so that no further XML escapes (entity references such as &amp;lt;) are necessary.</p>
<h3 id="complete-xml-example">Complete XML example</h3>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;test-data</span>
<span class="na">xmlns=</span><span class="s">"http://pmd.sourceforge.net/rule-tests"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://pmd.sourceforge.net/rule-tests https://pmd.sourceforge.io/rule-tests_1_0_0.xsd"</span><span class="nt">&gt;</span>
<span class="nt">&lt;test-code</span> <span class="na">disabled=</span><span class="s">"false"</span><span class="nt">&gt;</span>
<span class="nt">&lt;description&gt;</span>Just a description, will be used as the test name for JUnit in the reports<span class="nt">&lt;/description&gt;</span>
<span class="nt">&lt;rule-property</span> <span class="na">name=</span><span class="s">"somePropName"</span><span class="nt">&gt;</span>propValue<span class="nt">&lt;/rule-property&gt;</span> <span class="c">&lt;!-- optional --&gt;</span>
<span class="nt">&lt;expected-problems&gt;</span>2<span class="nt">&lt;/expected-problems&gt;</span>
<span class="nt">&lt;expected-linenumbers&gt;</span>5,14<span class="nt">&lt;/expected-linenumbers&gt;</span> <span class="c">&lt;!-- optional --&gt;</span>
<span class="nt">&lt;expected-messages&gt;</span> <span class="c">&lt;!-- optional --&gt;</span>
<span class="nt">&lt;message&gt;</span>Violation message 1<span class="nt">&lt;/message&gt;</span>
<span class="nt">&lt;message&gt;</span>Violation message 2<span class="nt">&lt;/message&gt;</span>
<span class="nt">&lt;/expected-messages&gt;</span>
<span class="nt">&lt;code&gt;</span><span class="cp">&lt;![CDATA[
public class ConsistentReturn {
public Boolean foo() {
}
}
]]&gt;</span><span class="nt">&lt;/code&gt;</span>
<span class="nt">&lt;source-type&gt;</span>java 1.5<span class="nt">&lt;/source-type&gt;</span> <span class="c">&lt;!-- optional --&gt;</span>
<span class="nt">&lt;/test-code&gt;</span>
<span class="nt">&lt;code-fragment</span> <span class="na">id=</span><span class="s">"codeSnippet1"</span><span class="nt">&gt;</span><span class="cp">&lt;![CDATA[
public class ConsistentReturn {
public Boolean foo() {
}
}
]]&gt;</span><span class="nt">&lt;/code-fragment&gt;</span>
<span class="nt">&lt;test-code&gt;</span>
<span class="nt">&lt;description&gt;</span>test case using a code fragment<span class="nt">&lt;/description&gt;</span>
<span class="nt">&lt;expected-problems&gt;</span>0<span class="nt">&lt;/expected-problems&gt;</span>
<span class="nt">&lt;code-ref</span> <span class="na">id=</span><span class="s">"codeSnippet1"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/test-code&gt;</span>
<span class="nt">&lt;/test-data&gt;</span>
</code></pre></div></div>
<div class="alert alert-info" role="alert"><i class="fas fa-info-circle"></i> <b>Note:</b> For better readability, the indentation should be 4 for spaces and no tabs.
Each test-code should be separated by a blank line. CDATA
sections are only required for the code snippets which should be formatted with indentation for
better readability. The description can be written directly without a CDATA section.</div>
<h2 id="using-the-test-framework-externally">Using the test framework externally</h2>
<p>It is also possible to use the test framework for custom rules developed outside the PMD source base.
In order to use the test framework you just need to reference the dependency <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd:pmd-test</code>.</p>
<p>For maven, you can use this snippet:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>net.sourceforge.pmd<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>pmd-test<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>7.8.0-SNAPSHOT<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>
<p>Then proceed as described earlier: create your test class, create your test cases and run the unit test.</p>
<p>There is one difference however: Since your package structure is probably different, youll need to register
the rule test manually, as SimpleAggregatorTst will fail to determine it correctly from the package and class names:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.example.pmd.rules</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">net.sourceforge.pmd.test.SimpleAggregatorTst</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">CustomRuleTest</span> <span class="kd">extends</span> <span class="nc">SimpleAggregatorTst</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">()</span> <span class="o">{</span>
<span class="n">addRule</span><span class="o">(</span><span class="s">"com/example/pmd/ruleset.xml"</span><span class="o">,</span> <span class="s">"CustomRule"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This will then search for a rule named “CustomRule” in the ruleset, that is located in “src/main/resources” under
the path “com/example/pmd/ruleset.xml”.</p>
<p>The test data should be placed in an XML file located in “src/test/resources” under the path
“com/example/pmd/rules/xml/CustomRule.xml”.</p>
<h2 id="how-the-test-framework-is-implemented">How the test framework is implemented</h2>
<p>The framework uses the <a href="https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests">dynamic test feature</a>
of JUnit5 under the hood, among a couple of utility classes:</p>
<ul>
<li>
<p><a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/PmdRuleTst.html#"><code>PmdRuleTst</code></a>: This is the base class for tests in PMDs code base. It is a subclass of
<a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/RuleTst.html#"><code>RuleTst</code></a> and just
contains the logic to determine the test resources based on the test class name.</p>
</li>
<li>
<p><a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/SimpleAggregatorTst.html#"><code>SimpleAggregatorTst</code></a>: This is a more generic base class for the test classes.
It doesnt register any test cases on its own. You can register your own rule tests.
It itself is a subclass of <a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/RuleTst.html#"><code>RuleTst</code></a>.</p>
</li>
<li>
<p>The maven module “pmd-test-schema” contains the logic to parse the XML files and provides a
<a href="https://docs.pmd-code.org/apidocs/pmd-test-schema/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/schema/RuleTestCollection.html#"><code>RuleTestCollection</code></a>. This in turn contains a list of
<a href="https://docs.pmd-code.org/apidocs/pmd-test-schema/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/schema/RuleTestDescriptor.html#"><code>RuleTestDescriptor</code></a>s. Each rule test descriptor describes a single test case.</p>
</li>
<li>
<p><a href="https://docs.pmd-code.org/apidocs/pmd-test/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/RuleTst.html#"><code>RuleTst</code></a>: uses the <a href="https://docs.pmd-code.org/apidocs/pmd-test-schema/7.8.0-SNAPSHOT/net/sourceforge/pmd/test/schema/TestSchemaParser.html#"><code>TestSchemaParser</code></a>
from module “pmd-test-schema” to parse the test cases, executes each
rule test descriptor and asserts the results. It defines a test method <code class="language-plaintext highlighter-rouge">ruleTests()</code> which is a test factory
and returns one dynamic test per rule test.</p>
</li>
</ul>
<h2 id="example-projects">Example projects</h2>
<p>See <a href="https://github.com/pmd/pmd-examples">https://github.com/pmd/pmd-examples</a> for a couple of example projects, that
create custom PMD rules for different languages including tests.</p>
<div class="tags">
<b>Tags: </b>
<a href="tag_extending.html" class="btn btn-outline-secondary navbar-btn cursorNorm" role="button">extending</a>
<a href="tag_userdocs.html" class="btn btn-outline-secondary navbar-btn cursorNorm" role="button">userdocs</a>
</div>
</div>
<footer>
<hr />
<div>
This documentation is written in markdown. <br />
If there is something missing or can be improved, edit this page on
github and create a PR:
<a
target="_blank"
href="https://github.com/pmd/pmd/blob/main/docs/pages/pmd/userdocs/extending/testing.md"
role="button"
><i class="fab fa-github fa-lg"></i> Edit on GitHub</a
>
</div>
<hr />
<div class="row">
<div class="col-lg-12 footer">
&copy;2024 PMD Open Source Project. All rights
reserved. <br />
<span>Page last updated:</span>
December 2023 (7.0.0)<br /> Site last generated: Nov 21, 2024 <br />
<p>
<img src="images/logo/pmd-logo-70px.png" alt="PMD
logo"/>
</p>
</div>
</div>
</footer>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
</div>
<!-- Sticky TOC column -->
<div class="toc-col">
<div id="toc"></div>
</div>
<!-- /.toc-container-wrapper -->
</div>
</div>
<script type="application/javascript" src="assets/jquery-3.5.1/jquery-3.5.1.min.js"></script>
<script type="application/javascript" src="assets/anchorjs-4.2.2/anchor.min.js"></script>
<script type="application/javascript" src="assets/navgoco-0.2.1/src/jquery.navgoco.min.js"></script>
<script type="application/javascript" src="assets/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js"></script>
<script type="application/javascript" src="assets/Simple-Jekyll-Search-1.0.8/dest/jekyll-search.js"></script>
<script type="application/javascript" src="assets/jekyll-table-of-contents/toc.js"></script>
<script type="application/javascript" src="js/tabstate.js"></script>
<script type="application/javascript" src="js/customscripts.js"></script>
</body>
</html>