Creating a JWT secured react app and Kotlin server (part 3)

Build tools, Unit tests, and Code coverage

The base application I generated from the Spring Boot Initializer was a Maven project. I had the option of Maven or Gradle, and I made a quick choice based on my knowledge at the time. You see, I’m a PHP developer. I’m used to there being one dominant package manager, Composer, and not having to make choices about which one to use. My experience with Gradle and Maven has been that Gradle is for Android projects and Maven for Server-side things. It’s not actually that clear cut, it’s just that Google has chosen Gradle to be the official build tool for Android, and maven is the original package manager so older projects run on maven.

I did experience a little bit of this package manager duality when developing JavaScript applications using either the Node Package Manager, or the stand-in replacement Yarn, developed by Facebook’s React team because npm was too slow, but yarn used the same package.json file as npm, so switching between the two only meant losing your package lock file.

Yarn’s biggest claim to fame is speed, and it seems that is what Gradle brings to the table also. Again, as a PHP developer I’m used to my code being interpreted at runtime, rather than being compiled with every code change, and it hadn’t even entered my mind that build speed would be a factor, but golly was I bored watching my application build over and over again to fix simple mistakes I’d made in my code.

The gradle website presents a very convincing demonstration of how much faster Gradle builds are, and that it actively avoids doing unnecessary work. It sings the praises of it’s “work avoidance mechanism”, and shouldn’t we all? I was sold on the promise of faster builds, and the result didn’t disappoint. Have a look here https://gradle.org/maven-vs-gradle/ if you’re interested in more reliable information.

Converting

I mentioned earlier the conversion from npm to yarn for an existing project was as simple as running `yarn` instead of `npm i`, but it’s not like that for Gradle, it’s a totally different structure. Maven has it’s pom.xml file that looks a bit like this

Maven pom.xml

And Gradle has a syntax that reminds me a bit of json and a bit of yaml. Apparently it’s a superset of Java and it’s called Groovy.

plugins {
    id 'application'
    id 'groovy-base'
}

dependencies {
    implementation project(':utilities')
    testImplementation 'org.codehaus.groovy:groovy-all:2.5.11'
    testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5'
}

application {
    mainClass = 'org.gradle.sample.app.Main'
}

The thing is that now with the introduction of Kotlin, everyone’s moving from Groovy DSL to Kotlin DSL which uses Kotlin syntax. I guess this is better, but what it means is that documentation is completely fragmented between maven XML, and the two different Gradle formats, and if you ever ask someone for help there’s a 66% chance they’ll tell you in some other syntax and you’ll have to covert it.

plugins {
    application
    `groovy-base`
}

dependencies {
    implementation(project(":utilities"))
    testImplementation("org.codehaus.groovy:groovy-all:2.5.11")
    testImplementation("org.spockframework:spock-core:1.3-groovy-2.5")
}

application {
    mainClass.set("org.gradle.sample.app.Main")
}

Apparently if you just run gradle in a maven repo, Gradle will do the coversion for you. Unforunately this creates a Groovey DSL, and all the kotlin docs give you instructions in Kotlin DSL, plus we need different plugins for Gradle, so it was easier in the end to re-generate the base project from the Spring Boot Initializer and copy in the new files. I ended up with this commit and this DSL

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
	id("org.springframework.boot") version "2.3.3.RELEASE"
	id("io.spring.dependency-management") version "1.0.10.RELEASE"
	kotlin("jvm") version "1.3.72"
	kotlin("plugin.spring") version "1.3.72"
	kotlin("plugin.jpa") version "1.3.72"
}

group = "com.chris-young"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-data-jpa")
	implementation("org.springframework.boot:spring-boot-starter-mustache")
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	developmentOnly("org.springframework.boot:spring-boot-devtools")
	runtimeOnly("com.h2database:h2")
	testImplementation("org.springframework.boot:spring-boot-starter-test") {
		exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
	}
}

tasks.withType {
	useJUnitPlatform()
}

tasks.withType {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "11"
	}
}

Code coverage

For code coverage I want to use a service called coveralls. I have chosen a Gradle plugin  that will upload reports to that service. To generate those reports we’ll use a Gradle plugin that produces coverage reports from unit tests called Jacoco.

To add the coverage report generator, we simply need to add the plugin here

plugins {
   id("org.springframework.boot") version "2.3.3.RELEASE"
   id("io.spring.dependency-management") version "1.0.10.RELEASE"
   id("java")
   id("jacoco")
   ...
}

The jacoco test report comes out looking like this.

Jacoco test report

Jacoco test report

This HTML report is very handy for local development – Next we need to enable XML report generation for export into coveralls.

tasks {
   jacocoTestReport {
      reports {
         xml.isEnabled = true
      }
   }
}

And then we’ll add the library to export to coveralls. Because this is a public repository, there’s no configuration required.

plugins {
   ...
   id("jacoco")
   id("com.github.kt3k.coveralls") version "2.10.2"
   ...
}

Lastly we’ll add a travis file (.travis.yml) to enable CI builds.

language: java

jdk:
- oraclejdk11

cache:
  directories:
    - $HOME/.gradle/caches/
    - $HOME/.gradle/wrapper/

install:
- ./gradlew assemble

script:
- ./gradlew test build

after_script:
  - ./gradlew jacocoTestReport coveralls

Theoretically all that’s needed is language: java and travis will automatically detect the build.gradle file and use gradle to build and test the  repo, but since we want to use a newer version of Java and Gradle than the ones that ship with travis, and we want to do some specific step at the end, it’s better if we are a bit more specific about all the build steps.

Now when we push to GitHub, Travis CI will run the build script and upload the test reports to Coveralls.

resolvconf
install_jdk
Installing oraclejdk11
0.00s
git.checkout
0.44s$ git clone --depth=50 --branch=main https://github.com/darkbluesun/auth-demo-auth.git darkbluesun/auth-demo-auth
0.01s$ export TERM=dumb
cache.1
Setting up build cache
$ java -Xmx32m -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
$ javac -J-Xmx32m -version
javac 11.0.2
install
21.04s$ ./gradlew assemble
25.22s$ ./gradlew test build
> Task :compileKotlin UP-TO-DATE
> Task :compileJava NO-SOURCE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestKotlin
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test
2020-09-13 02:10:57.345  INFO 4737 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-09-13 02:10:57.345  INFO 4737 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2020-09-13 02:10:57.356  INFO 4737 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-09-13 02:10:57.358  INFO 4737 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table if exists user CASCADE 
Hibernate: drop sequence if exists hibernate_sequence
2020-09-13 02:10:57.587  INFO 4737 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2020-09-13 02:10:57.592  INFO 4737 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-09-13 02:10:57.623  INFO 4737 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-09-13 02:10:57.625  INFO 4737 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2020-09-13 02:10:57.635  INFO 4737 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2020-09-13 02:10:57.638  INFO 4737 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2020-09-13 02:10:57.639  INFO 4737 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown initiated...
2020-09-13 02:10:57.645  INFO 4737 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown completed.
> Task :bootJar UP-TO-DATE
> Task :inspectClassesForKotlinIC UP-TO-DATE
> Task :jar SKIPPED
> Task :assemble UP-TO-DATE
> Task :check
> Task :build
BUILD SUCCESSFUL in 24s
6 actionable tasks: 2 executed, 4 up-to-date
The command "./gradlew test build" exited with 0.
before_cache.1
0.00s$ rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
before_cache.2
0.00s$ rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache.2
store build cache
after_script
11.19s$ ./gradlew jacocoTestReport coveralls
Done. Your build exited with 0.

Once the coverage report is uploaded it looks like this:

Coveralls code coverage

Coveralls code coverage

Here’s the full commit on Github.

Next I’ll add some actual functionality and get that sweet 100% coverage.

Comments Closed

Comments are closed.