Exercise 6: Software Metrics

This exercise explores how the source code analyzer PMD computes software metrics and provides warnings to developers about potential design quality issues in Java applications. You will not need to download and install PMD manually, as this will be handled for you by Gradle.

Preparation

  1. Download the code for this exercise into the top level of your repository and unzip the file. This should create a directory named exercise6, inside of which are several files and subdirectories. The code in the initial subdirectory is the car rental example from the lectures and Introduction to Refactoring exercise, before any refactorings have been applied. The code in the final subdirectory represents the point after all of the refactorings in that exercise have been applied.

    Remove the Zip archive, then use Git to add, commit and push the newly added files.

  2. Spend a few minutes examining the two samples of code, to remind yourself of how refactoring has changed things.

  3. Open the file initial/build.gradle in a text editor. Line 3 directs Gradle to use the PMD Plugin. Lines 25-29 configure the plugin:

    pmd {
      ignoreFailures = false
      ruleSetFiles = files("../config/pmd/ruleset.xml")
      ruleSets = []
    }
    

    The first of these settings ensures that the build will fail if there are any rule violations1. The second setting specifies where the rules we wish to use can be found. The last setting clears out PMD’s default rules, ensuring that only the rules we have specified will be used.

Examining PMD Rules

  1. Open the file config/pmd/ruleset.xml in a text editor and study it.

  2. Read the PMD documentation on these rules. Make sure you understand what will be measured when you run PMD. You may find it useful to also consult PMD’s index of Java code metrics for more context.

Running PMD

  1. In a terminal window, run PMD on the initial, unrefactored code like so2:

    ./gradlew :initial:check
    

    (If you are using the standard Windows command prompt, omit the ./ from the above command.)

  2. PMD will write details of rule violations to a report in initial/build/reports/pmd/main.html. Open this file in a web browser. You should see 3 different violations reported. PMD has picked up on the fact that Car is a very simple ‘data class’, with no useful logic. It has also identified issues with the length and complexity of the statement() method in Customer.

  3. Now run PMD on the refactored code:

    ./gradlew :final:check
    

    Open the report in final/build/reports/pmd/main.html in a separate browser tab and compare it with the report generated for the unrefactored code.

    Notice how the issues originally reported by PMD have disappeared. Refactoring has moved some logic into Car, making it more useful in the application, so it is no longer considered to be a data class. Refactoring has also improved statement(), making it shorter than the threshold on length set in the rules.

    However, PMD is reporting a new rule violation, relating to the cyclomatic complexity of setPriceCode() in Car. If you examine the implementation of this method, you’ll see there is nothing particularly complex about it. It consists of little more than a single switch statement, with 4 short, simple branches. This highlights a limitation of cyclomatic complexity: it doesn’t measure the cognitive complexity of code.


  1. In a real scenario, we probably wouldn’t want to fail the entire build just because some rules had been violated. The thresholds we choose for rule violation are somewhat arbitrary, and violation doesn’t necessarily point to a serious problem in the code. ↩︎

  2. Note that Gradle’s check target will attempt to check the code against the PMD ruleset and then run the unit tests. You don’t see any of the tests running here because ignoreFailures has been set to false. You can set it to true to run the tests regardless.

    If you only want to check for PMD rule violations and never run the tests under any circumstances, you can use the pmdMain target. ↩︎