Simple Kotlin smoke-tests (Retrofit, JUnit, and bug free production)

Knock out bugs with Kotlin smoke-testing. Run tests against live sites to find bugs before they do damage.

A common concern for any developer is whether their code works. Unit tests catch many simple errors but the only way to know whether an application is truly functioning is with end-to-end tests.

These are tests that emulate user actions to verify aspects of your app that are key to its success (think user sign-up, payment processing, support contacts). You want to know if these actions are really working on production and not silently failing. So an end-to-end test is written that invokes these actions on a live site and verifies the results. This is sometimes called a smoketest as it can catch any fires early.

There are many end-to-end test frameworks out there but a great approach I’ve found is using Kotlin, Retrofit and JUnit5. This is because it is fast, type-safe, and has a lot of batteries included. If you’ve never used Kotlin before it’s really easy to get the hang of, especially if you’ve done Java or Typescript before.

An example smoke-test

To demonstrate E2E testing in Kotlin, let’s think of an example application and the key features we want to test. For this post we’ll imagine a microservice REST API that allows purchases. We’ll test the ability to get a payment method (such as a credit card). It’s just an example though as you could test anything really.

Project setup

/your-project
├── build.gradle
├── settings.gradle
└── src
└── test
├── kotlin
│ └── demo
│ └── MyTest.kt
└── resources
└── application.yml

Starting a Kotlin project from scratch can be a pain so lets use Spring Initializr to bootstrap our E2E test project. You can invoke it via HTTP.

curl https://start.spring.io/starter.tgz -d language=kotlin -d type=gradle-project | tar -xzvf -

This command should request a Kotlin Spring Gradle project as a tgz and unzip it in your working directory. Open it up in your favorite editor.

Adding dependencies

Next we need to add Retrofit and a JSON parser as dependencies to build.gradle. These will allow us to call our example microservice REST API.

// add to build.gradle
dependencies {
// retrofit
testCompile ‘com.squareup.retrofit2:retrofit:2.3.0’
// lib for parsing JSON api responses
testCompile ‘com.squareup.retrofit2:converter-gson:2.3.0’
}

Calling APIs

To test the ability to fetch a users payment method we’ll use Retrofit to call our API and JUnit to verify the results.

Retrofit is a really clever HTTP library from Square. You use interfaces and annotations to describe an API and then build it into a concrete client using Retrofit reflection. Don’t worry, it’s not as scary as it sounds. Here’s how you use it.

First create an interface for your API. Then a method for an API endpoint — say, getPaymentMethod to fit with our example. Then you annotate the method with an HTTP method and path (@GET(“/users/me/payment-method”) for example). Lastly, add any additional query params or headers and give the method a response type. Wrap your response entity in Retrofit’s Call class to make the entire method executable.

Here’s the end result:

interface Api {
@GET(“/users/me/payment-method”)
fun getPaymentMethod(
@Header(“authorization”) bearerAuth: String
): Call<ResponseBody>
}

Pretty neat! But how do we use it? Well we instantiate the interface with Retrofit by passing in a baseUrl. We can actually add this instantiation method to the Api interface itself as a static method.

interface Api {

// factory methods
companion object {
fun create(baseUrl: String): Api {
// here we set the base url of our API
val retrofit = Retrofit.Builder().baseUrl(baseUrl)
// add the JSON dependency so we can handle json APIs
.addConverterFactory(GsonConverterFactory.create())
.build()
// here we pass a reference to our API interface
// and get back a concrete instance
return retrofit.create(Api::class.java)
} }

// other functions ignored…
}

So all together now.

val api = Api.create(“https://yourapi.com")
val response = api.getPaymentMethod(“authToken”).execute()
// how to get the response values
val body = response.body()
val code = response.code()

Verifying results

Now its time to actually call our API’s payment method endpoint and verify the result. So in the src/test directory add a new class for our test.

class CreditTest {@Test
fun `can get payment method` () {
// call the api
val api = Api.create(“yourapi.com”)
val response = api.getPaymentMethod(“authToken”).execute()
// verify the response is OK
assertThat(response.code()).isEqualTo(200)
}
}

What’s the catch?

Well, did you notice use of the @Header(“authorization”) in our method signature? That’s because many APIs require OAuth2 authentication. For your test you may want to create a test user and save their authorization token or login details in your test configuration.

This can lead to additional problems however. Auth tokens often expire so logging in with a real username and password is probably a more reliable way to get auth tokens during tests. But that means storing usernames and passwords in your code. It also means that your one test user will have an account littered with records and entities created during tests.

What can we do about this…

Emulating user sign-up

In an ideal world every test would run with a brand new user whose account is fresh. That way each test user would be isolated from any other concurrent test actions. Setting this up manually is a big job! You’d need to set up an SMTP server with a custom domain, implement an SMTP client in your test suite and then route emails to the correct address... No, no, no.

Luckily, MailSlurp provides an affordable API for generating unique email addresses on the fly. You can then send and receive, read and write emails from these addresses via REST or a number of API client.

We can use MailSlurp in our smoketests to create new isolated users for every test case. Here’s the logic:

  • A test suite starts
  • Test invokes MailSlurp API to create a unique email address
  • Test uses the address to sign up as a new user on the API
  • Test confirms the users email address (by receiving the confirmation email and submitting the verification code)
  • Test is able to trigger actions as a new user and verify hard to test async actions like password reset or support contact.

MailSlurp is a simple REST API for sending and receiving emails so we can call it via Retrofit too!

interface MailSlurp {

@POST(“/inbox”)
fun createInbox(
@Header(“x-api-key”) apiKey: String
): Call<Inbox>

data class Inbox(val id: String, val emailAddress: String)
}

Pretty simple. You need an API key to use MailSlurp, but it is free to start. For sending and receiving emails, check out the documentation.

Conclusion

There you have it, a very simple E2E test that calls a real API using Retrofit. With any end-to-end tests you often come up against issues when trying to test user sign up and login. As shown above, MailSlurp is a nice addition in these cases. Enjoy!

--

--

MailSlurp | Email APIs for developers

Test Email API for end-to-end with real email addresses. Support for NodeJS, PHP, Python, Ruby, Java, C# and more. See https://www.mailslurp.com for details.