Chapter 9. Maintaining Code Quality

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

We need these tools to write better code. Better code means 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 are tools already available for Java and Groovy projects to analyze and check source code, such as Checkstyle, JDepend, PMD, FindBugs, CodeNarc, and Sonar. Gradle has plugins for each of these tools. In this chapter, we will take a look at the plugins and learn how to use them in our projects.

Using the Checkstyle plugin

If we are working on a Java project, and apply the Java plugin to our project, we get an empty task with the name 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. And not only the tasks we write ourselves, but the plugins also, can add new dependency tasks to the check task.

We will see in this chapter 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 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:

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
...

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]

...

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

We cannot execute these tasks yet, because we have to add a Checkstyle configuration file to our project. This file contains the rules that we want applied for our code. The plugin will look for a file checkstyle.xml in the directory config/checkstyle 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'
  plugin 'checkstyle'
}

repositories.mavenCentral()

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

:compileJava
:processResources UP-TO-DATE
:classes
:checkstyleMain
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:0: File does not end with a newline.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:0: Missing package-info.java file.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:8:5: Missing a Javadoc comment.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:9: Line has trailing spaces.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:10:5: Missing a Javadoc comment.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:10:29: Parameter args should be final.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:14: Line has trailing spaces.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:15: Line has trailing spaces.
[ant:checkstyle] /samples/chapter9/sample/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] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:17:5: Missing a Javadoc comment.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:17:42: 'greeting' hides a field.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:21:5: Method 'greet' is not designed for extension - needs to be abstract, final or empty.
[ant:checkstyle] /samples/chapter9/sample/src/main/java/gradle/sample/JavaSample.java:21:5: Missing a Javadoc comment.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':checkstyleMain'.
> Checkstyle rule violations were found. See the report at / samples/chapter9/sample/build/reports/checkstyle/main.xml.

* 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: 4.889 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 file with the violations in the build/reports/checkstyle directory.

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

We can configure the Checkstyle plugin with the checkstyle{} script block or the 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 set the ignoreFailures property to true, so the build will not fail even after Checkstyle finds errors:

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

checkstyle {
  ignoreFailures = true
}

While this book was being written, Gradle used Checkstyle 5.5. To change the version, we can set the toolVersion property to another version. For example, if, in the future, Checkstyle 5.6 is released, then we simply have to change the toolVersion property and don't need a new Gradle version.

In the following example build file, we set the Checkstyle version to 5.4 by changing the toolVersion property. We might need to do this for legacy projects too:

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

checkstyle {
    toolVersion = 5.4
}

To change the Checkstyle configuration file, we can set the property configFile to a different value. 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 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.

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

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

checkstyle {
    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 syntax ${propertyName}. We can set the value for such a property by 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 with the name tabWidth, for example, we can set the value with the following example build file:

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

// Set checkstyle options, that are used by
// all checkstyle tasks.
checkstyle {
  configProperties = [tabWidth: 10]
}

We use the checkstyle{} script block to change the properties for all the checkstyle tasks in a project. But we can also configure individual checkstyle tasks in our build file. We have got 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'
  plugin 'checkstyle'
}

repositories.mavenCentral()

// 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. So once we run the check task, Gradle will invoke the checkstyleApi task as well.

In this example build file, we create a new source set with the name api:

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

sourceSets {
  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
...

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 report file build/reports/checkstyle/main.xml. We can configure this in our build file. We can change the reports output directory 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, the destination file for the checkstyleMain task, and disables report generation for the checkstyleTest task:

apply {
  plugin 'java'
  plugin 'checkstyle'
}

repositories.mavenCentral()

checkstyle {
  reportsDir = file("${buildDir}/checkstyle-output")
}

checkstyleTest {
  reports.xml.enabled = false
}

checkstyleMain {
  reports {
    xml {
      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.22.77.63