Setting up Kotlin, JUnit, Mockito, and Gitlab CI for Libgdx Projects

I just spent the weekend trying my hand at Libgdx. For fun I started a project using the following:

These are the same tools I use at my day job and other Android projects! It's awesome to jump into a new framework with familiar tools.

Kotlin

Jetbrains generously added a guide on how to install the Kotlin plugin for Intellij or Android Studio.

Android Studio will automatically configure your buildscripts to add and enable Kotlin in your project.

I like Kotlin. The Kotlin step in this guide isn't mandatory as you can always stick with Java.

JUnit and Mockito

Setting up unit tests in Android Studio is easy.. or at least for vanilla Android Projects. What about Libgdx? While gdx-setup.jar sets up your Libgdx project so you could work on it using Intellij, there's really no documentation on how to do add unit tests easily.

For this post we'll integrate the JUnit unit testing framework, and the Mockito-Kotlin library for mocking.

Add JUnit and Mockito-Kotlin dependencies

In ./build.gradle:

project(":core") {
 dependencies {
  testCompile "junit:junit:$junit_version"
  testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version
 }
}

Make sure you use testCompile instead of compile! This makes sure that dependencies used for unit testing are only compiled for unit tests.

Tell Libgdx where your unit tests are

In ./core/build.gradle:

Replace this line

sourceSets.main.java.srcDirs = [ "src/" ]

With

sourceSets.main.java.srcDirs = [ "src/main/java/" ]
sourceSets.test.java.srcDirs = [ "src/test/java/" ]
sourceSets.test.resources.srcDirs = [ "src/test/res/" ] *

This line is optional, I know about this from the JUnit + Libgdx guide from techduke.io.

If you don't do this, you may get the Unresolved reference message upon compile time and fail. Android Studio imports the correct libraries in your unit tests, but gradle still has to find where your unit testing folder is!

Test Logging

Enable logging so you and other developers know which tests passed or failed! In ./core/build.gradle, add the following at the end of the file:

test {
    testLogging {
        showStandardStreams = true
        events "standardOut", "passed", "skipped", "failed"
        exceptionFormat = 'full'
    }
}

Or if you're feeling fancy, use the recommendation from this StackOverflow answer!

You should create the relevant directories as well. In ./core/src, create a test/kotlin/*mypackagename* and a test/res/ directory. *mypackagename* corresponds to your project's package name, ex: com/codingdoodles/sampleproject

In Android Studio, hit Sync and Build!

Unit Testing

We'll be writing our first unit test in Kotlin, and our first mock with Mockito-Kotlin. Assuming we have a real class that has the drawDebug(shapeRenderer: ShapeRenderer) method, such as:

fun drawDebug(shapeRenderer: ShapeRenderer) {
 shapeRenderer.rect(x, y, SIZE, SIZE)
}

We can mock the shapeRenderer parameter and verify shapeRenderer.rect(x, y, SIZE, SIZE) is called by:

val shapeRenderer : ShapeRenderer = mock()

@Test
fun onDrawDebug_createRect() {
 subject.drawDebug(shapeRenderer)
 verify(shapeRenderer).rect(x, y, SIZE, SIZE)
}

But what about mocking Kotlin classes? Remember that Kotlin classes are final by default. Unfortunately, Mockito cannot mock final classes. If you do, you'll get this error:

Mockito cannot mock/spy following:  
  - final classes
  - anonymous classes
  - primitive types

To get around this, should we declare our classes as open for the actual code? Surely, there should be a better way. Fighting Kotlin and using it like Java sorta makes using Kotlin such a waste of time.

When in Rome, do as the Romans do..

Instead, lets just declare these classes as open only during testing. That way, we keep the Kotlin-ness of our code.

To do that, the good folks at Jetbrains have created kotlin-allopen to help facilitate this. It lets us use annotations to tell gradle to set these Kotlin classes to open for tests.

In core/build.gradle, add this dependency and apply plugin:

apply plugin: 'kotlin-allopen'

dependencies {
 classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
 ...
}

Be sure to sync dependencies. In your code, create an annotation class called TestOpen:

package com.mypackage.myapp

annotation class TestOpen

By the way, TestOpen could be anything you want to call this helper annotation.

And finally, annotate each class you want to mock with @TestOpen. Proceed to mock these classes. Gradle shouldn't return the error this time!

Gitlab CI

For Android projects, Gitlab CI provides a .yml template for easy facilitation. However, there is no such thing on Libgdx! ~~Who uses unit tests on games anyways.~~

Create a .gitlab-ci.yml file in the root of your project. In .gitlab-ci.yml, add the following:

image: openjdk:8-jdk

stages:
  - build
  - test

build:
  stage: build
  script:
    - ./gradlew clean build

unitTests:
  stage: test
  script:
    - ./gradlew test

It's that easy! You can check and see how your jobs are doing using Gitlab's UI. Pretty slick and intuitive in my opinion.

gitlab-ci-libgdx

gitlab-ci-libgdx

For some reason caching doesn't work for me. Each job/stage downloads my dependencies, wasting precious time :( But free is free, and for this project I am perfectly happy.

Build passed! My sample game project is looking a lot like a proper day job now :) gitlab-ci-libgdx

The End

I hope this mini guide helps you use proper software engineering processes like Continuous Integration and unit testing for your Libgdx projects.

tags: code