Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

Specifying Kotlin tests with Spek

The 12th day’s post of Mercari Advent Calendar 2019 is brought to you by @kinnerapriyap from the US@Tokyo Android team.

Writing unit tests usually means having to suffer through a lot of boilerplate code. I’m guilty of sometimes writing test cases with an internal understanding of how the code works, resulting in lesser test coverage.

However, ideal test cases are expected to be pure code that express only the test cases and their necessary aspects. To define such a flow in which intention is validated, we can use Specification frameworks like Spek combined with Unit Testing frameworks to test our apps.

What is Spek?

Spek is a Kotlin-based specification framework for JVM. Remember, Spek is not a testing library, which means that we can combine Spek with JUnit, Mockito and even UI tests.

Testing the what instead of the how: By focusing on testing logic without actually describing its flow, we validate intention by differentiating between attributes relevant to the test case and those not.

Writing Spek tests with JUnit

To set up Spek, check out the official documentation here.

Let’s first write a data class in kotlin and call it Person.

To write unit tests with proper coverage for isPersonRetired(), we would need at least the following tests:

  • when isActive is true and age is greater than RETIREMENT_AGE

  • when isActive is true and age is lesser than RETIREMENT_AGE

  • when isActive is false and age is greater than RETIREMENT_AGE

  • when isActive is false and age is lesser than RETIREMENT_AGE

In each test, we would need to initialise Person with all values, regardless of whether or not they’re needed for the particular test.

With Spek

Let’s write tests in Spek now. With nesting lambdas, we can reduce the duplication in test code and increase readability.

To start, we’ll create the Spek test suite.

We will now initialise Person in the test suite with the fixture beforeEachGroup.

Spek will execute beforeEachGroup block immediately before every group, allowing us to modify only the required attributes for each group.

We’ll now use the JUnit Assertions class to write the actual test. Spek allows us to use whatever assertion framework we're most comfortable with.

For when isActive is false and age is lesser than RETIREMENT_AGE:

You can look at the entire test suite here.

f:id:kinnerapriyap:20191209171908p:plain

I prefer the Specification style using describe/it (more flexibility) but you can also try the Gherkin style to write Spek tests.

Patching up tests

One of the pains with unit tests is that if —or when— you change a class, you often spend a good deal of time patching up all your unit tests or refactoring a lot of mock code.

Let’s add hasPersonWorked to the Person data class.

With regular unit tests, we would need to modify Person initialisation in every test. 🙀

With Spek

With Spek, we can fix initialisation by adding hasPersonWorked once in the fixture beforeEachGroup.

We’re not done yet because hasPersonWorked is required by the current test cases and we will need to make changes to the rest of the test suite.

Adding to existing tests

If we were to also check hasPersonWorked for isPersonRetired() with regular unit tests, it would not only require us to double the number of tests to add hasPersonWorked, but also make the test names too long to read.

Test names would then be when isActive is true, age is greater than 60 and hasPersonWorked is true and so on, which would get longer if more conditions are added.

Every additional test layer is another place to make changes in the future. Using Spek significantly reduces risk or iteration time in order to pay for itself.

With Spek

NoSuchMethodError

A very common error with Spek is java.lang.NoSuchMethodError. Unfortunately, restarting Android Studio will not resolve this issue. 😛

This error happens when the Spek version defined in your dependencies does not match the version used in the Android Studio plugin. It’s good practice to keep a tab on the dependencies that your project is using and check for compatibility when migrating to newer versions of Spek.

Conclusion

I’ve been amazed by how readable (and joyful) unit testing can be by writing declarative test code and I hope you can give Spek a try too!

Tomorrow's blog —the 13th in the Advent Calendar— will be written by @catatsuy. Hope you are looking forward to it! 😸