Developer's Diary
Software development, with Terry Ebdon
20-Jul-2020 Back to basics

Infrastructure

I took a step back, to put development infrastructure in place. Every non-trivial app, i.e. anything more than a quick script, needs:

  • Version control
  • A build file.
  • Unit tests.
  • Test coverage reports.
  • Static analysis.
  • Logging

I started the project with git init and gradle init, so the repo and build files are there along with a template Spock test. Gradle also provides a basic version of continuous integration, that's good enough for my personal projects. If I need more I can look at integrating Travis CI into Git or using Jenkins.

Unit tests

Eclipse has been complaining about Spock; I fixed that by switching to JUnit. I don't object to using Spock, this was just the quickest way of fixing the problem. I checked with the team lead, me, and he was happy with this approach.

Groovy's JUnit support gives very clean test code, with nice reporting:

@groovy.util.logging.Log4j2('logger')
class ShootLocationsTest extends GroovyTestCase {
private final int numLocations = 7;
void testGetDisplayNames() {
String[] displayNames = new ShootLocations().displayNames
assert displayNames.size() == numLocations
String lastDn = ''
displayNames.each {
assert it[5..7] == ' * '
assert it > lastDn
lastDn = it
}
}
}

Test coverage

I first used JaCoCo on my Trk21 project. I've been very happy with it. It's trivial to configure, just add it as a Gradle plugin:

plugins {
id 'groovy'
id 'application' // support for building an application.
id 'jacoco' // Instrument build & add coverage reporting tasks
}

Static analysis

CodeNarc is my "go to" tool for this. Enabling it in Gradle is trivial, but I always end up fiddling with the configuration to solve minor problems.

I enabled CodeNarc and copied the Trk21 configuration file into NewPostProd.

plugins {
id 'groovy'
id 'codenarc' // Static analysis & reporting.
id 'application' // support for building an application.
id 'jacoco' // Instrument build & add coverage reporting tasks
}
codenarcMain {
configFile = file("$rootDir/config/codenarc/codenarc.groovy")
ignoreFailures = true
}
codenarcTest {
configFile = file("$rootDir/config/codenarc/codenarc.groovy")
ignoreFailures = true
}

That worked, but gave a stack of "compilation errors". These weren't real errors, just CodeNarc getting confused. The code compiled and tested fine, the CodeNarc report was generated as expected. But these annoying error messages were on the console:

...
None: 22: unable to resolve class ShootLocation
@ line 22, column 13.
void add( ShootLocation sl ) {
^
...
None: 5: unable to resolve class groovy.transform.Canonical
@ line 5, column 1.
import groovy.transform.Canonical
^
...

Some of the CodeNarc rules were causing these messages. I identified the failures by disabling rules until the error disappeared. This is very quick with binary search debugging. I found the enhanced.xml section was the source of the problem.

// rulesets/enhanced.xml <-- Breaks CodeNarc 1.4
// CloneWithoutCloneable
// JUnitAssertEqualsConstantActualValue
// MissingOverrideAnnotation
// UnsafeImplementationAsMap

I had to disable all four tests to eliminate the spurious error messages. I need to revisit this, probably by grabbing the latest example RuleSet from the CodeNarc web site and diff debugging.

Logging

Apache Log4J2 is my preferred logging mechanism. Groovy has built-in annotation support for various loggers, making Log4J very easy to use. Groovy also removes the need for explicitly checking the log level.

@groovy.util.logging.Log4j2('logger')
class PhotoShoot {
// ...
void exportScript() {
logger.info this
// ...
}

19-JUL-2020 👈 Top of page 👉 02-AUG-2020

Buy Me a Coffee at ko-fi.com
© 2020 Terry Ebdon.

Find me coding on GitHub, networking on LinkedIn, answering questions on Stack Exchange and hanging out on twitter.