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.
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 :)
The End
I hope this mini guide helps you use proper software engineering processes like Continuous Integration and unit testing for your Libgdx projects.