At WillowTree, we’re adamant about having the best-possible systems in place to ensure that the code we ship is error-free. We’re always looking for ways to improve this process, like writing unit tests and using languages like Kotlin which have built-in safety features.

divider 600

assertk

To help with both of those things, today we are releasing assertk, an assertion library built specifically for Kotlin, modeled after AssertJ. assertk gives you fluent assertions with an extensible api.

import assertk.assert

@Test
fun myTest() {
  assert("test").isEqualTo("test")
}

divider 600

Why Kotlin needs a dedicated assertion library

So why use a new assertion library when you can use existing Java assertion libraries such as AssertJ or Truth in Kotlin? Because assertk takes advantage of one of Kotlin’s killer features: extension functions. With assertk, adding your own assertions is fast and simple:

fun Assert<Person>.hasAge(expected: Int) {
  assert(actual.age, name="age").isEqualTo(expected)
}

@Test
fun myTest() {
  val person = Person("Sue", 22)
  assert(person).hasAge(24)
  // -> expected [age] to be:<24> but was:<20>
}

No classes to subclass and no interfaces to implement, just a one line function and you’re done! This makes it easy to not only build of a large collection of generic assertions, but to create your own domain-specific ones just for your project. divider 600

Features

Let’s run through a few of assertk’s features with a simple todo app. Todo.run() takes a command like “list” or “add My Todo” and returns the resulting list.

object Todo {
  val items = mutableListOf<Item>()

  fun run(args:String): String { ... }
}
data class Item(val name: String)

You can assert multiple things on the object by passing a lambda. It will report all assertions that fail.

@Test
fun testList() {
  Todo.items += Item("test1")
  Todo.items += Item("test2")
  val result = Todo.run("list")

  assert(result) {
    contains("1) test1")
    contains("2) test2")
  }
}

You can also extend this behavior to multiple assertions with an assertAll.

@Test
fun testAdd() {
  Todo.items += Item("test1")
  val result = Todo.run("add test2")

  assertAll {
    assert(Todo.items).containsExactly(Item("test1"), Item("test2"))
    assert(result) {
      contains("1) test1")
      contains("2) test2")
    }
  }
}

You can test against multiple values by using a table. Each row is your test data and forAll will run your assertion per row.

@Test
fun testRemove() {
  Todo.items += Item("test1")
  Todo.items += Item("test2")

  assertAll {
    tableOf("number", "result")
      .row("2", "1) test1")
      .row("1", "").forAll { number, result ->

         assert(Todo.run("remove $number")).isEqualTo(result)
      }
      assert(Todo.items).isEmpty()
   }
}

divider 600

V 0.9

So what’s the state of the project? While we are releasing as version 0.9, the api will be stable barring any major issues. The “0.9” is just meant to represent that there are still some assertions that need to be added, and more tests need to be written for existing assertions. As our focus is in mobile apps, our logical next step is to add android-specific assertions. divider 600

Help us grow assertk!

We are already using assertk in a few production projects, but we’d love your help! If you are interested in contributing, check out our contributing guidelines.