Chapter 9. Maintaining Code Quality

While working on a project, we want to have some kind of tooling or process in place, which we can use to see whether our code follows certain standards; either our code has no common coding problems or it calculates the complexity of the code.

We need these tools to write better code. Better code means that it will be easier to maintain, and this lowers the cost of maintaining the code. In a project team, we want to make sure that the code follows the same standards defined by the project team. A company could define a set of standards that developers need to follow as a condition for the project to be started.

There tools are already available for Java and Groovy projects to analyze and check the source code, such as CheckstyleJDependPMDFindBugs, and CodeNarc. Gradle has plugins for each of these tools. In this chapter, we will take a look at the following plugins and discuss how to use them in our projects:

  • Checkstyle
  • PMD
  • FindBugs
  • JDepend
  • CodeNarc

Using the Checkstyle plugin

If we are working on a Java project, and we apply the Java plugin to our project, we will get an empty task, named check. This is a dependency task for the build task. This means that when we execute the build task, the check task is executed as well. We can write our own tasks to check something in our project and make it a dependency task for the check task. So if the check task is executed, our own task is executed as well. Not only the tasks that we write ourselves, but also the plugins can add new dependency tasks to the check task.

In this chapter, we will see that most plugins will add one or more tasks as a dependency task to the check task. This means that we can apply a plugin to our project, and when we invoke the check or build task, the extra tasks of the plugin are executed automatically.

Also, the check task is dependent on the test task. Gradle will always make sure that the test task is executed before the check task, so we know that all source files and test source files are compiled, and tests are run before the code is checked. To add the Checkstyle analysis to our project, we simply have to apply the checkstyle plugin, as follows:

apply plugin: 'java' 
apply plugin: 'checkstyle' 

If we invoke the tasks task from the command line, we can see that new tasks have been added by the plugin:

$ gradle tasks --all
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
...
Verification tasks
------------------
check - Runs all checks. [classes, test, testClasses]
checkstyleMain - Run Checkstyle analysis for main classes
checkstyleTest - Run Checkstyle analysis for test classes
test - Runs the unit tests. [classes, testClasses]
...
BUILD SUCCESSFUL
Total time: 1.194 secs

The checkstyleMain and checkstyleTest tasks are added as dependencies for the check task. The tasks run the Checkstyle analysis for the main and test classes.

We cannot execute these tasks yet as we have to add a Checkstyle configuration file to our project. This file contains the rules that we want to apply to our code. The plugin will look for a checkstyle.xml file in the config/checkstyle directory in our project. This is the default location and filename, but we can change it.

Let's create a configuration file with the following content:

<?xml version="1.0"?> 
<!DOCTYPE module PUBLIC 
    "-//Puppy Crawl//DTD Check Configuration 1.3//EN" 
    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> 
<module name="Checker"> 
    <module name="JavadocPackage"/> 
    <module name="NewlineAtEndOfFile"/> 
    <module name="RegexpSingleline"> 
      <property name="format" value="s+$"/> 
      <property name="minimum" value="0"/> 
      <property name="maximum" value="0"/> 
      <property name="message" value="Line has trailing spaces."/> 
    </module> 
 
    <module name="TreeWalker"> 
      <module name="IllegalImport"/> 
      <module name="RedundantImport"/> 
      <module name="UnusedImports"/> 
      <module name="AvoidNestedBlocks"/> 
      <module name="EmptyBlock"/> 
      <module name="LeftCurly"/> 
      <module name="NeedBraces"/> 
      <module name="RightCurly"/> 
      <module name="DesignForExtension"/> 
      <module name="FinalClass"/> 
      <module name="HideUtilityClassConstructor"/> 
      <module name="InterfaceIsType"/> 
      <module name="VisibilityModifier"/> 
    </module> 
 
</module> 

The checkstyle plugin does not add the required library dependencies to our project automatically. We need to add an appropriate repository to our project so that the checkstyle plugin can download all the dependencies.

Let's create the following example build file and add the repository definition:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    // Add repository so Checkstyle 
    // dependencies can be downloaded. 
    jcenter() 
} 

Now we can run the check task and see the output:

$ gradle check
:compileJava
:processResources UP-TO-DATE
:classes
:checkstyleMain
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:0: File does not end with a newline.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:0: Missing package-info.java file.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:9: Line has trailing spaces.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:14: Line has trailing spaces.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:15: Line has trailing spaces.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:17:5: Method 'setGreeting' is not designed for extension - needs to be abstract, final or empty.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:21:5: Method 'getGreeting' is not designed for extension - needs to be abstract, final or empty.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:25:5: Method 'greet' is not designed for extension - needs to be abstract, final or empty.
[ant:checkstyle] /Projects/gradle-bookChapter9/Code_Files/checkstyle/src/main/java/gradle/sample/JavaSample.java:29:5: Method 'equals' is not designed for extension - needs to be abstract, final or empty.
:checkstyleMain FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at: file:///Projects/gradle-book/Chapter9/Code_Files/checkstyle/build/reports/checkstyle/main.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 1.296 secs

The checkstyleMain task has been executed and the build has failed because our code doesn't follow our Checkstyle rules. In the output, we can see all the violations of the rules. Gradle will also create an XML and HTML file with the violations in the build/reports/checkstyle directory.

If we don't want the build to fail, we can use the ignoreFailures property of the checkstyle tasks. The checks are still executed and the report files are generated, but the build will not fail.

We can configure the checkstyle plugin with the checkstyle{} script block or checkstyle property in a Gradle build. The script block accepts a configuration closure, where we can change the properties. In the following build file, we will set the ignoreFailures property to true, so the build will not fail even after Checkstyle finds errors:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
// Configuration for Checkstyle. 
checkstyle { 
    // The build will not fail if there 
    // are violations found. 
    ignoreFailures = true 
} 

To change the version of Checkstyle that is used by Gradle is setting the toolVersion property in the checkstyle{} configuration block. We can assign a different version than the one that is available by default in Gradle.

In the following sample build file, we will use the toolVersion property to use Checkstyle version 5.7:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
checkstyle { 
    // Use version 5.7 of Checkstyle. 
    toolVersion = '5.7' 
} 

The plugin also adds a new dependency configuration, named checkstyle. We will use this configuration to add dependencies that are needed by the Checkstyle tool.

To change the Checkstyle configuration file, we can set the configFile property to a different value in the checkstyle{} configuration block. The default value is config/checkstyle/checkstyle.xml. We can, for example, copy the sun_checks.xml configuration file from a Checkstyle distribution to the config/checkstyle directory. We will set the configFile property with the value of this new file and our code is checked using the rules from the sun_checks.xml configuration file.

The following sample build file shows that we have referenced another Checkstyle configuration file:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
// Configuration for Checkstyle. 
checkstyle { 
    // Use a different configuration file. 
    configFile = file('config/checkstyle/sun_checks.xml') 
} 

A Checkstyle configuration supports property expansion. This means that the configuration file has variable property values with the ${propertyName} syntax. We can set the value for such a property using the configProperties property of the Checkstyle configuration closure. This property accepts a map, where the keys are the property names from the Checkstyle configuration file and the values are the property values. If the Checkstyle configuration file has a property, named tabWidth; for example, we can set the value with the following example build file:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
// Configuration for Checkstyle. 
checkstyle { 
    // Define key-value pairs to replace properties 
    // in the configuration file. 
    configProperties = [tabWidth: 10] 
} 

We use the checkstyle{} script block to change the properties for all the checkstyle tasks in a project. However, we can also configure individual checkstyle tasks in our build file. We have the checkstyleMain and checkstyleTest tasks and we can alter their configuration just like any other task.

Let's create the following example build file and change the properties of the checkstyleTest task, which will override the properties set in the checkstyle{} script block:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
// Set checkstyle options, that are used by 
// all checkstyle tasks. 
checkstyle { 
    configFile = file('config/checkstyle/sun_checks.xml') 
} 
 
// Reconfigure the checkstyleTest task. 
checkstyleTest { 
    configFile = file('config/checkstyle/test.xml') 
    ignoreFailures = true 
} 

If we have defined custom source sets in our build, then the checkstyle plugin automatically adds a checkstyle<SourceSet> task to the project. If our source set is named api, then we can invoke the checkstyleApi task to only check this source set. The checkstyleApi task is also added as a dependency task for the check task. Therefore, once we run the check task, Gradle will invoke the checkstyleApi task as well.

In the following example build file, we will create a new source set, named api:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
sourceSets { 
    // Add new source set with 
    // the name api. This will 
    // add a task checkstyleApi. 
    api 
} 

If we invoke the tasks task, we can see in the output that a newly created task checkstyleApi is added, which is a dependency task for the check task:

$ gradle tasks --all
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
...
Verification tasks
------------------
check - Runs all checks. [apiClasses, classes, test, testClasses]
 checkstyleApi - Run Checkstyle analysis for api classes
 checkstyleMain - Run Checkstyle analysis for main classes
 checkstyleTest - Run Checkstyle analysis for test classes
test - Runs the unit tests. [classes, testClasses]
...

The report XML files that are generated are placed in the build/reports/checkstyle directory. The name of the files is based on the source set name. So the checkstyleMain task will generate the build/reports/checkstyle/main.xml report file. We can configure this in our build file. We can change the directory containing the reports with the reportsDir property. We can change the destination file for a specific checkstyle task with the destination property. We can also disable the report generation with the enabled property for a given task.

The following sample build file changes the reporting directory and the destination file for the checkstyleMain task and disables report generation for the checkstyleTest task:

apply plugin: 'java' 
apply plugin: 'checkstyle' 
 
repositories { 
    jcenter() 
} 
 
checkstyle { 
    // Change the output directory. 
    reportsDir = file("${buildDir}/checkstyle-output") 
} 
 
checkstyleTest { 
    // Disable running CheckStyle for the test classes. 
    reports.xml.enabled = false 
} 
 
// Configure the checkstyle task for the main source set. 
checkstyleMain { 
    reports { 
        xml { 
            // Change the destination and filename of the 
            // XML file with results generated by CheckStyle. 
            destination = file("${checkstyle.reportsDir}/checkstyle.xml") 
        } 
    } 
} 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.106.233