Effective tool chain for kotlin js testing

Coding in kotlin and targeting javascript platform is a bit strange. Half of the tools you need are written in java or kotlin (but ran in a JVM). The other half comes from the javascript frenetic ecosystem. 
How can we combine these tools to pick the best of the two worlds? In this post I’m going to share my choices for kotlin-js development and detail the testing part. My goals are to:

  1. Have an effective development workflow
  2. Easily manage browsers tests
  3. Use automated build compatibles with continuous integration.

When choosing my tools I generally try to use a best-of-breed approach, using each tool for what it was designed, and trying to take the best one.

Here is the big picture:

This tool chain will be part of my live session at KotlinConf the 2nd of november 2017.

I created a github repository as a support for this post. Just clone it locally and run gradlew build to test the concepts presented here.

Gradle

There are few build systems compatible with kotlin development. The mains ones are maven, gradle and kobalt. I generally keep maven for simple builds. Gradle is more powerful but more difficult to manage. I didn’t run enough tests on kobalt for now.

Both maven and gradle have kotlin plugins maintained by Jetbrain team.

The main reasons for choosing gradle for kotlin-js development are:

  1. JetBrains seems to have a faster pace development on gradle plugins: Dead Code Elimination is currently only available on gradle. The kotlin javascript incremental compilation will also be delivered first on gradle.
  2. It’s really fast. Gradle has a mechanism that only re-launch the needed tasks. With the continuous mode it allows you to work on your sources, having gradle rebuilding in background only what is needed for your project.

The kotlin2js plugin usage is straightforward. You don’t have to configure it a lot. One of the most important parameter is the module kind which defines how the generated javascript files can be used. The default is plainbut my default choice is umd (Unified Module Definitions) which is compatible with node.js (common.js), require.js and fallbacks to plain.

apply plugin: 'kotlin2js'
compileKotlin2Js {
kotlinOptions{
moduleKind = 'umd'
}
}

For your tests on your project you must add a dependency on kotlin-test-js.

dependencies {
testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
}

This dependency will provide you the annotation @Test and some assertions functions.

import kotlin.test.* 
class SimpleTest {     
@Test fun testFoo() {
assertEquals(10, foo())
}

During kotlin2js compilation the annotation will be used to generate javascript code compatible with the main javascript testing frameworks: jasmine, jest, qunit, mocha, tape.

After the compilation task you have to move in the javascript world. Your code is now compiled in two javascript modules : one for your main code and the other one for test code. To execute the test module on your main module you now need to use a javascript testing framework.

Mocha

I didn’t take a long time to choose the testing framework. The main features of these javascript frameworks are not used during kotlin js development. Your tests are written in kotlin with a basic common denominator. The testing framework is just used as a runtime execution and report tool. You can’t use the specificities of the framework.

I took this list to check the current solutions:

During my evaluation I find that mocha had a good report rendering and it was enough for me.

Terminal rendering of mocha tests.

Karma

When you plan to have your code running inside browsers it is really interesting to test it inside different browsers. This is the job of karma. It provides you a runtime environment to run your test code.

The configuration file allows you to define the locally installed browsers on which the code should be ran: chrome, safari, firefox, internet explorer, …

module.exports = function(config) {
config.set({
browsers : ['Chrome', 'Firefox']
});
};

It can also run your code on PhantomJs, the headless browser which is useful for automated builds.

Okay, we now have our code transpiled in javascript, we have chosen the frameworks. How can we assemble all that?

Yarn

The bridge between our main build script (gradle) and the javascript runtime will be done with a gradle plugin: the gradle-node-plugin

This plugin enabled you to use a lot of NodeJS-based technologies as part of your build without having NodeJS installed locally on your system. It integrates the following NodeJS-based system with Gradle:
NodeJS
Yarn
Grunt
Gulp

From this quick documentation, we have to make some decisions. NodeJS and NPM defined a way of managing javascript dependencies and scripts through a package.json file. The newcomer tool, Yarn, uses this file with a better strategy to manage dependencies with a local cache making it a lot more performant. As the gradle-node-plugin allows us to use it, it will be our choice.

To use the plugin in gradle we add:

apply plugin: 'com.moowork.node'
node {
download = true
}
task yarnInstall(type: YarnTask) {
args = ['install']
}

Yarn will be automatically downloaded during the first launch of gradle build.

We add the task yarnInstall which will execute the command yarn install. Yarn uses the default package.json configuration file to install locally all the needed tools : karma, mocha, …

The last task of our gradle build is :

task runKarma(type: YarnTask, dependsOn: [populateNodeModules, yarnInstall]) {
args = ['test']
}

test.dependsOn runKarma

The command yarn test rely on the testscript defined in the package.json file:

"scripts": {
"test": "karma start"
}

Again, using default configuration files, we don’t have to provide any argument. When starting, karma looks for a karma.conf.jswhere the launched test configuration is defined.

The complete tool is efficient. I usually have gradle running in continuous mode on the project I’m working on. It allows to have a really quick feedback on my code.

coding test with immediate feedback

Conclusion

Testing is only a part of kotlin-js development workflow. There are other issues with other solutions. I will present most of them during my live session at KotlinConf the 2nd of november 2017.

If you want to have a look to a more complex kotlin build, you can star and follow our data2viz project on github. Our goal is to provide a “d3js like” multiplatform library. We will soon have kotlin multiplatform builds.

Don’t hesitate to share your point of view, correct me or ask questions.