iwillteachyoukotlin
Shares

Download The Ultimate Tutorial: Getting Started With Kotlin

350 pages of hands-on tutorial right into your email inbox. Learn how to use state of the art development environment and build a full-fledged command-line application. You will receive free updates to the tutorial. Start learning Kotlin today!

All posts by Oleksii Fedorov

How to Try Kotlin in Java Backend Codebase Without Risk

Shares

Okay, a show of hands.

Who’s heard of this lovely and astounding programming language Kotlin, and wanted to try it out on the real backend project…

and couldn’t?

Yeah, most of us. This is common. The team discusses that everyone really wants to go Kotlin, and they still decide to create that new codebase in Java.

You and your fellow colleagues are afraid that this will go crazy wrong, right?

And you are right to have such a feeling, as you don’t have enough confidence yet to make the switch. What if the whole team is still learning this new technology, and there is an unexpected challenge that nobody can resolve for weeks?

Yeah… tricky situation.

Now, you’re probably quite a bit into this project, and your backend codebase is all in verbose Java, and you think: “No way I can try Kotlin now! Not until next one…”

Wrong.

There is no point in waiting until the next opportunity because of the high chances that it’ll turn out the same!

Now, what if I told you that you can still try out Kotlin together with your teammates in this codebase without any risks, and no strings attached?

Let me explain.

Mixing Java and Kotlin code

With Kotlin’s 100% bidirectional interoperability, it’s possible to mix Java and Kotlin code easily.

What this means, is that you can convert a single file from Java to Kotlin to get the feels of what it’ll look like. And everything will work just like before.

Now, what if I told you that such a conversion is one hotkey away?

Let me show an example. Imagine you had this simple service class in your Java backend application:

// QuizService.java
package com.iwillteachyoukotlin.quizzy;

import org.springframework.stereotype.Service;

@Service
public class QuizService {

    private final QuizRepository quizRepository;

    public QuizService(QuizRepository quizRepository) {
        this.quizRepository = quizRepository;
    }

    public Quiz create(Quiz quiz) {
        if (quiz.id != null) {
            throw new BadRequestException("id should be empty");
        }

        return quizRepository.save(quiz);
    }

    public Quiz getQuiz(int quizId) {
        return quizRepository
                .findById(quizId)
                .orElseThrow(() -> new NotFoundException("quiz not found"));
    }
}

Now, you can press CMD+ALT+SHIFT+K (or CTRL+ALT+SHIFT+K), or you could use Convert Java File to Kotlin File action:

Open Find Action tool, type convert to kotlin, press enter, and see the file fully convert to kotlin automatically

The result is the following code. Notice how both constructor and field definition has merged into a single declaration in Kotlin:

// QuizService.kt
package com.iwillteachyoukotlin.quizzy

import org.springframework.stereotype.Service

@Service
class QuizService(private val quizRepository: QuizRepository) {

    fun create(quiz: Quiz): Quiz {
        if (quiz.id != null) {
            throw BadRequestException("id should be empty")
        }

        return quizRepository.save(quiz)
    }

    fun getQuiz(quizId: Int): Quiz {
        return quizRepository
                .findById(quizId)
                .orElseThrow { NotFoundException("quiz not found") }
    }
}

Note for Lombok users:

Use Delombok on this file before converting to Kotlin.

Of course, you need to add Kotlin support to your build tool before you can even run this code:

Add Kotlin support to Gradle

(Note: refer to this guide if you’re using Maven)

Add a Gradle plugin appropriately:

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.3.11"
}

Note for Spring Boot users:

You need to add this plugin, so that Kotlin classes will open automatically where needed, so that Spring Boot can use reflection on them:

id "org.jetbrains.kotlin.plugin.spring" version "1.3.11"

And if you’re using JPA/Hibernate entities, you’ll need this plugin:

id "org.jetbrains.kotlin.plugin.jpa" version "1.3.11"

This adds the default constructor for your entities.

Also, you’ll need to add a dependency on Kotlin standard library and reflection library:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "org.jetbrains.kotlin:kotlin-reflect"
    // …
}

Now you have one Kotlin file among the sea of Java files. Java classes are calling this Kotlin code, and this Kotlin code is calling to Java code.

And it all perfectly works. And all tests pass!

Now, simple automatic conversion is ok and all, but it’s not enough to really leverage the power of Kotlin.

1. Use Elvis operator instead of most null checks

If you had any null check anywhere that returns or throws an exception, for example:

fun update(quiz: Quiz): Quiz {
    if (quiz.id == null) {
        throw BadRequestException("id should not be empty")
    }

    return quizRepository.save(quiz)
}

These null checks that either throw or return are usually called guard if statements. In Kotlin these can be done more succinctly with “Elvis” operator ?::

fun update(quiz: Quiz): Quiz {
    quiz.id ?: throw BadRequestException("id should not be empty")

    return quizRepository.save(quiz)
}

2. Convert Optionals to nullable

Another example that can be simplified with Kotlin is the usage of Optional type:

fun getQuiz(quizId: Int): Quiz {
    return quizRepository
            .findById(quizId)
            .orElseThrow { NotFoundException("quiz not found") }
}

Here we’re going to use another method of our repository that returns either a found object or null:

fun getQuiz(quizId: Int): Quiz {
    return quizRepository.findByIdOrNull(quizId)
            ?: throw NotFoundException("quiz not found")
}

As you can see, here we use the Elvis operator, as well. Quite handy, isn’t it?

3. Make JUnit test names more readable

Let’s imagine that the application above had this test in its integration test suite:

@Test
public void failsToCreate_whenIdIsProvided() throws Exception {
    // ARRANGE
    final Quiz quiz = new Quiz(
            42,
            "title",
            "description",
            "cta",
            "https://example.org/image.png"
    );

    // ACT
    final ResultActions actions = mockMvc.perform(post("/quizzes")
            .contentType(APPLICATION_JSON_UTF8)
            .content(objectMapper.writeValueAsString(quiz)));

    // ASSERT
    actions.andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.message", equalTo("id should be empty")));
}

Now, after automatic conversion to Kotlin it’ll look something like this:

@Test
@Throws(Exception::class)
fun failsToCreate_whenIdIsProvided() {
    // ARRANGE
    val quiz = Quiz(
            42,
            "title",
            "description",
            "cta",
            "https://example.org/image.png"
    )

    // ACT
    val actions = mockMvc!!.perform(post("/quizzes")
            .contentType(APPLICATION_JSON_UTF8)
            .content(objectMapper!!.writeValueAsString(quiz)))

    // ASSERT
    actions.andExpect(status().isBadRequest)
            .andExpect(jsonPath("$.message", equalTo("id should be empty")))
}

First, we can throw away this @Throws annotation immediately. It’s mostly useless in Kotlin.

And now, we can use actual human-readable sentences in the method names using backticks:

@Test
fun `create quiz - fails to create when id is provided`() {
    // …
}

My general structure is: “{method name or use case name} – {expected outcome} when {condition}.”

4. Use lateinit instead of nullable

In cases, when something is being provided later (like dependency injection, or initialized in the set-up section of the test suite), you should use lateinit instead. It’s much cleaner.

Look, this code is a result of automatic conversion:

@SpringBootTest
@RunWith(SpringRunner::class)
class QuizzesIntegrationTest {

    @Autowired
    private val context: WebApplicationContext? = null

    @Autowired
    private val objectMapper: ObjectMapper? = null

    private var mockMvc: MockMvc? = null

    private var quizId: Int = 0

    @Before
    fun setUp() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(context!!)
                .build()
    }

    // …

}

All these fields are initialized later, AND before the first usage, so we can tell the compiler about that:

@Autowired
private lateinit var context: WebApplicationContext

@Autowired
private lateinit var objectMapper: ObjectMapper

private lateinit var mockMvc: MockMvc

private var quizId: Int = 0

Now, the problem is that all these nullable values were unsafely unwrapped everywhere in the test suite with !! operator:

a lot of warnings about unnecessary non-null (!!) assertion

Of course, we can fix them all manually, but these are things that tools should fix for us.

And they do, look:

use intent actions menu and select remove unnecessary non-null assertion, it removes !! automatically

It still would be annoying to go through each occurrence though. So we should just apply code cleanups to the whole file:

use find action tool and type code cleanup, the tool window shows up, select a single file only there, and proceed, all unnecessary non-null assertions disappear from the whole file

5. Use named arguments for readability

Now, you see this snippet of code:

// ARRANGE
val quiz = Quiz(
        42,
        "title",
        "description",
        "cta",
        "https://example.org/image.png"
)

We can make this code much more descriptive if we were to use named arguments, like so:

use intent actions menu and choose to add names to call arguments, every argument now is named

Now, you’ll get this cute little readable snippet of code. Also, you can re-order the arguments when you pass them as you wish, and as it makes more sense.

// ARRANGE
val quiz = Quiz(
        id = 42,
        title = "title",
        description = "description",
        ctaText = "cta",
        imageUrl = "https://example.org/image.png"
)

Unfortunately, to make this feature work, the class Quiz can’t be a Java class, it has to be in Kotlin, so we’ll have to convert the entity below to Kotlin:

@Entity(name = "quizzes")
@Data
@RequiredArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer"})
public class Quiz {
    @Id
    @GeneratedValue
    public Integer id = null;

    public final String title;
    public final String description;
    public final String ctaText;
    public final String imageUrl;

    Quiz() {
        title = "";
        description = "";
        ctaText = "";
        imageUrl = "";
    }
}

This is a result of full (auto + manual) conversion:

@Entity(name = "quizzes")
@JsonIgnoreProperties("hibernateLazyInitializer")
data class Quiz(
        @Id
        @GeneratedValue
        var id: Int? = null,

        val title: String,
        val description: String,
        val ctaText: String,
        val imageUrl: String
)

As you can see we’re getting rid of all the Lombok stuff and using a data class now because it can do most of the things, you would need from the entity.

6+. There is so much more you can improve!

If you want to learn more about how good Kotlin code will look like, I highly recommend to go through the Kotlin Koans.

You can do that right in your browser, or in your IDE if you wish.

Now, remember, at the beginning of this post, I’ve promised the “Without Risk” part, didn’t I?

Trying out without any risks whatsoever

Now, if I decided not to make the switch to Kotlin yet (because I still need to convince my colleagues, for example), I can use Local History feature of my IDE to go back in time to when I started playing with Kotlin:

local history tool window

To open that tool in IntelliJ, you can use the contextual menu on the whole project in the project structure tool. There is an option Local History > Show History there:

right click on the project name in project structure tool, choose Local History - Show History

When you’ve found the place where you started playing with Kotlin, you can revert to the first Kotlin-related change, for example:

choosing revert option on a specific change in the past

Now, there is not a single Kotlin file, and all the tests are passing. This is a handy feature in general if you screwed something up, and haven’t made a commit in Git in a while.

Did that spike your curiosity about Kotlin?

I have written a 4-part (350-pages total) “Ultimate Tutorial: Getting Started With Kotlin” (+ more to come), and you can get it as a free bonus by becoming a member of my monthly newsletter.

On top of just Kotlin, it is full of goodies like TDD, Clean Code, Software Architecture, Business Impacts, 5 WHYs, Acceptance Criteria, Personas, and more.

—Sign up here and start learning how to build full-fledged Kotlin applications!

Thank you and ask for you!

Thank you so much for reading this article! I hope you enjoyed it. Please, tell me what you think about this in the comments!

Also, it would make me so much happier share this post with your friends and colleagues who you think might benefit from it. Or you could share it on your favorite social media!

You are welcome to read my blog about Kotlin, and my blog about TDD and best software engineering practices.

And let’s connect on LinkedIn: I post short weekly updates about software developer’s productivity and happiness, teamwork, mental health, and a bit about Kotlin.

If you want to hear more of my opinions, follow me on Twitter.

Kotlin: How Can a Programming Language be so Young And so Mature at The Same Time?

In this article, we’re going to explore why young programming languages with modern features can’t be adopted quickly. Additionally, we’re going to take a look at one exceptional example that got specific parameters right to be both young, modern and mature, just ready for adoption at small and big scale.

DISCLAIMER: Some details are omitted intentionally. Keep your mind open when reading. If something seems naive, or not entirely true, bear in mind that the author thought into both the front and back sides of the coin, and the unseen third side of the coin, as well. You’re welcome in comments for a more in-depth discussion—it’s going to be worth your time for sure!

We all know that most of us, software developers, love to chase after shiny things just as much as we like to build them. This is not different for programming languages.

And, in this day and age, new young programming languages pop up like these mushrooms:

Why new modern languages can’t be adopted quickly?

A distinct feature of a young language is that creators and community gets the chance to experiment with different features and designs to potentially fix some problems of the existing mature languages. Mind you, any solution to any problem will bring minimum two more subtle issues. So this cycle of continuous improvement will never end.

While young languages can explore more advanced features and designs, they usually don’t have a robust ecosystem of libraries, frameworks, and tooling. Moreover, when the community develops these, people will still have to learn them, and that means that they (albeit temporarily) will be much less productive. And I’m not even going to talk about potential vulnerabilities, bugs, and so on.

Now, wouldn’t it be great if the new young modern language could rely on the existing mature ecosystem? Wouldn’t it be fantastic if using existing libraries, frameworks and tooling was entirely natural and not awkward at all?

If you ask me these questions, I’ll tell you that you’re perhaps describing Kotlin. Kotlin is a programming language where you can naturally use any library, framework or tool from the JVM ecosystem without any awkwardness.

Now, there were languages before, that could do that as well, like Scala, Groovy, Clojure, and so on. And they all got something wrong: natural use.

Why these languages don’t want to rely on the existing mature ecosystem?

Either it wasn’t natural to use the same framework or library, or it was even impossible to use the same tooling. And in the scenario where it would be possible to use same tooling, framework, and libraries naturally; somehow, the majority of the community went out there and built new tools, frameworks, and libraries (I’m looking at you, Scala, and sbt).

My hunch is that this happens because most of the early adopters of these new languages (“promised replacements for Java”) have somewhat an adversarial relationship with Java language and JVM ecosystem.

They hate it.

As early adopters, they set the playing field, because they are in the vast majority in the community during the first few years of the new young language. This way, they get to write all the brand new shiny tools, frameworks and libraries. And they are the ones who start new projects in these languages at actual companies and startups.

This way, the community style sets in: unique tooling, frameworks, and sets of libraries that people use. Sometimes, these are even designed on purpose to be radically different from their older alternatives from the JVM community.

This makes the learning curve very steep, and so the language gets adopted only by like-minded people, and poor developers who just have no choice but to maintain the codebases where this set of tools is being used already.

How to attract different early adopters that will be happy to leverage existing ecosystem?

Kotlin is radically different from this approach. The playing field rule number one: “100% bidirectional interoperability with Java and JVM ecosystem.”

Thus the community attracts fellows who are not looking to do everything in the entirely different approach; instead, they want a more modern language, AND they want to transfer their skills, knowledge, and experience from working with the mature ecosystem like JVM.

These early adopters don’t want to rewrite their existing codebases entirely because they are large, so being able to write only new code in Kotlin, and keep older code in Java helps a lot!

Small story of a fellow software engineer

I’m going to give you a tangible example:

Clara is a software engineer, working for a big organization, mostly working with Java, JVM, and Spring Boot. Clara’s professional level is senior and perhaps even beyond that:

  • she applies best software practices,
  • knows how to design simple software iteratively,
  • can solve any complex problem you throw at her,
  • knows her audience when writing the code (current and expected in the future level of her team members),
  • excellent communicator and team player,
  • teaches and mentors other developers,
  • can take on the role of a team lead today,
  • and lives the life of a life-long learner.

Her current development workflow includes building the application and automating development tasks with Gradle, and she gets her coding done within a modern IDE like IntelliJ. She doesn’t have any of the adversarial feelings towards any of these tools and technologies.

Quite the opposite: she loves them!

Sometimes though, she feels feature envy to all these cool modern programming languages where you could have one-liner data classes, less boilerplate in general, and the null-pointer exception is not a dreaded threat at every step.

And here is Kotlin programming language that recently became “production-ready.”

So she tries out to build a tiny web app (a tad more complicated than “Hello world”). And it seems like she can still use the same build tool—Gradle; and she still can use any of her favorite libraries, and much more critical, she can use the same web framework—Spring Boot.

And using all these things is not awkward at all, and feels just as natural, as in their “home” environment—Java.

Now, she has the next question: “This language is great but quite new… do I have to lose all the great features of my IDE now?” Because that is what happens to most modern languages—the IDE support is lagging way… way behind.

And now she gets pleasantly surprised: 95% of the IDE features that she needs are there and are supported:

  • basic and advanced refactorings,
  • jump to definition,
  • find usages,
  • running application and tests from IDE,
  • running a single test in the current context,
  • debugger,
  • code completion, even cross-language,
  • etc.

And this makes so much sense because the language is developed by the same company that develops the IDE (JetBrains). This certainly ensures that the tooling will be at least somewhat up to speed with the new modern language.

“This all sounds great!” Clara thinks, “Do I have to rewrite my application at work now fully to Kotlin?”

Most important playing field rules fostering the adoption

And she gets pleasantly surprised here as well. All she really needs to do is to add Kotlin Gradle plugin (few lines of change in a single file), and she can start adding Kotlin files to the source code base. All the old files, written in Java, are still working correctly.

Moreover, cross-language calls are working out of the box: she can call Java code from Kotlin, and Kotlin code from Java; AND it all feels just as natural. At almost no point, the calling code is even aware that it is calling one or the other because there is no difference.

That is what making “100% interoperability” a most significant playing field rule right from the beginning can do for the new modern language!

So if you’re going to design a new programming language, I suggest you consider imbuing it as one of the top priorities, as it will allow for much better adoption. And what creator of the library, tool, or language doesn’t want this kind of adoption superpower? 😉

As you could notice, Clara is not looking for an entirely radically different approach to coding, and instead, she is looking for a way to keep 75% of what is working well, and perhaps use a few more modern features of the language to improve the other 25% significantly.

This could be reducing the amount of the boilerplate by letting the language figure out the obvious so that you don’t have to specify it every time. This could also be a different decision to make everything an expression, which makes specific scenarios much more ergonomic and more comfortable to read, for example, try-catch block, where you need to set a variable in the “try” part and rethrow an enhanced error in the “catch” part.

These things and more are improving the readability significantly.

Additionally, the ability to have first-class immutable variables and nullable/non-nullable types eliminates whole classes of possible mistakes and errors. It is fantastic!

Stand on the shoulders of the giants!

Most of the modern languages are created to be used standalone, detached from the existing library and tooling ecosystem, either by design or “forced” by community guidelines or culture.

These modern languages might see some adoption, but they will eventually die out or stay in the small niche because the majority of professionals will just continue using the mature ones because they have better support for literally everything. There are rare, infrequent, exceptions from this rule.

If the new programming language wants to stay strong, it needs to stand on the shoulders of the giants.

So, instead of going against the former mature language, and doing everything differently, leverage as much as you can from the mature ecosystem, and add more value on top of that. This way, people, when learning this language don’t have to give up anything, and they only stand to gain— a real win-win situation!

Did that spike your curiosity about Kotlin?

I have written a 4-part (350-pages total) “Ultimate Tutorial: Getting Started With Kotlin” (+ more to come), and you can get it as a free bonus by becoming a member of my monthly newsletter.

On top of just Kotlin, it is full of goodies like TDD, Clean Code, Software Architecture, Business Impacts, 5 WHYs, Acceptance Criteria, Personas, and more.

—Sign up here and start learning how to build full-fledged Kotlin applications!

Thank you and ask for you!

Thank you so much for reading this article! I hope you enjoyed it. Please, tell me what you think about this in the comments!

Also, it would make me so much happier if you could share this post with your friends and colleagues who you think might benefit from it. Or you could share it on your favorite social media!

You are welcome to read my blog about Kotlin, and my blog about TDD and best software engineering practices.

And let’s connect on LinkedIn: I post short weekly updates about software developer’s productivity and happiness, teamwork, mental health, and a bit about Kotlin.

If you want to hear more of my opinions, follow me on Twitter.


cover image source: pexels

How to Make Your Tests Read Like Business Rules (w/ infix functions)

See the code above? It’s actually a runnable JUnit4 test in Kotlin. It’s also a description of business rules readable in English.

Mind. Blown.

Let’s dive in how did I get there.

Starting from the first failing test (Red)

class GameTest {
    @Test
    fun `scissors cut paper`() {
        val game = Game()

        val winner = game.play(Throw.SCISSORS, Throw.PAPER)

        assertThat(winner).isEqualTo(Winner.FIRST_PLAYER)
    }
}

At this point, the test doesn’t compile: Game, Throw, and Winner are not defined. Let’s create classes and enums for these:

class Game {
    fun play(first: Throw, second: Throw): Winner {
        TODO("not implemented")
    }
}

enum class Throw {
    SCISSORS, PAPER
}

enum class Winner {
    FIRST_PLAYER
}

Now it compiles. Before we run the test suite, let’s make an assumption of how it’s going to fail. It’s going to fail with the “not implemented” error. Let’s run the test suite and see:

Great. Exactly as expected.

This technique is essential to TDD. It has a name—“Calling a Shot.” If we call a shot, and something unexpected happens, that means that we have a mistake in our test. That is a way to test your tests when writing them.

Making the first test pass (Green)

Let’s make it pass with the simplest implementation. Just return the FIRST_PLAYER as a winner:

fun play(first: Throw, second: Throw): Winner {
    return Winner.FIRST_PLAYER
}

If we run the tests now, they all pass:

Making the test pass with the simplest implementation is what makes TDD produce near 100% test coverages. And I’m not talking about lines or branches—about TRUE test coverage.

Basically, the simplest implementation is when no matter what you “subtract” from it—at least one test will fail. In a sense, writing such an implementation you’re exposing test cases that you didn’t even think about before, and likely wouldn’t have written them as automated tests.

Looking for refactoring opportunities (Refactor)

At this point, all our tests are passing. This is called a “Green” stage of the TDD cycle. In this stage, we are free to clean up and refactor anything we want. So we take a look at both production code and test code:

Everything looks fine right now—there is no duplication, names are alright, and there is nothing weird. So we loop back to the first stage of TDD again—“Red” stage.

The second failing test (Red)

The next test flows naturally from our current simplest implementation. Basically, the question that we need to ask at this point—how can we prove the current implementation wrong?

Since we are returning the constant value, we can prove the implementation wrong by providing inputs where the expected winner is different. We can do it if we were to reverse SCISSORS and PAPER arguments:

@Test
fun `scissors cut paper - reverse`() {
    val game = Game()

    val winner = game.play(Throw.PAPER, Throw.SCISSORS)

    assertThat(winner).isEqualTo(Winner.SECOND_PLAYER)
}

Winner.SECOND_PLAYER is not defined yet, so we’ll create it as an enum constant:

enum class Winner {
    FIRST_PLAYER, SECOND_PLAYER
}

Now, before we are going to run the test, we are going to call a shot. We expect that there will be an assertion error in the second test. The expected winner should be SECOND_PLAYER, but the actual will be FIRST_PLAYER since we are returning the same constant always at the moment.

Now, we’re ready to run the test suite:

org.junit.ComparisonFailure: 
Expected: SECOND_PLAYER
Actual: FIRST_PLAYER

at rps.GameTest.scissors cut paper - reverse(GameTest.kt:22)

That is precisely what we expected.

Making the second test pass (Green)

Let’s make this test pass by wrapping the existing return statement in the simplest if statement, and returning the SECOND_PLAYER right after:

fun play(first: Throw, second: Throw): Winner {
    if (first == Throw.SCISSORS) {
        return Winner.FIRST_PLAYER
    }

    return Winner.SECOND_PLAYER
}

If we rerun the test suite, everything should be green now. Let’s look for refactoring opportunities now.

Increasing readability of the test and production code (Refactor)

Those pesky fully-qualified names make the code slightly less readable. I think we can omit them by using direct imports. We’ll start with the test code:

import rps.Throw.*
import rps.Winner.*

// ...

@Test
fun `scissors cut paper`() {
    val game = Game()

    val winner = game.play(SCISSORS, PAPER)

    assertThat(winner).isEqualTo(FIRST_PLAYER)
}

@Test
fun `scissors cut paper - reverse`() {
    val game = Game()

    val winner = game.play(PAPER, SCISSORS)

    assertThat(winner).isEqualTo(SECOND_PLAYER)
}

And let’s do the same to the production code:

import rps.Throw.*
import rps.Winner.*

// ...

fun play(first: Throw, second: Throw): Winner {
    if (first == SCISSORS) {
        return FIRST_PLAYER
    }

    return SECOND_PLAYER
}

Now that we’ve done the refactoring, we should run the test suite. It is still green! Let’s take a look if we have any duplication we should get rid of:

Reducing the repetition in the test code (Refactor)

The first thing that stands out in our test code is that we’re creating the game object in every test in precisely the same way.

The rule of thumb for refactoring the duplication is that we need three occurrences. Here though, you can already tell that the next test will need to create the same object in the same way. So we might as well refactor right now.

Extracting the variable as a private field of the class works here:

class GameTest {
    private val game = Game()

    @Test
    fun `scissors cut paper`() {
        val winner = game.play(SCISSORS, PAPER)

        assertThat(winner).isEqualTo(FIRST_PLAYER)
    }

    @Test
    fun `scissors cut paper - reverse`() {
        val winner = game.play(PAPER, SCISSORS)

        assertThat(winner).isEqualTo(SECOND_PLAYER)
    }
}

Right after making this atomic change, we should run the tests. And they still pass!

Now, the next duplication is calling to game.play with two throw arguments and then asserting the result is correct. Again, there are only two occurrences so far, but we can easily tell that every test will have the same snippet of the code.

Let’s extract the private method assertGame:

private fun assertGame(first: Throw,
                       second: Throw,
                       expected: Winner) {
    val actual = game.play(first, second)

    assertThat(actual).isEqualTo(expected)
}

And the tests that are now using this function look like this:

@Test
fun `scissors cut paper`() {
    assertGame(SCISSORS, PAPER, FIRST_PLAYER)
}

@Test
fun `scissors cut paper - reverse`() {
    assertGame(PAPER, SCISSORS, SECOND_PLAYER)
}

Let’s verify that we didn’t break anything by rerunning the test suite. It’s all green—great!

Now if you look carefully, these two tests are actually describing a single business rule, and they are simple one-liners. And we can already tell that every rule of the Rock-Paper-Scissors game will come in a pair like this.

Let’s put these two lines together and get rid of the “reversed” test:

@Test
fun `scissors cut paper`() {
    assertGame(SCISSORS, PAPER, FIRST_PLAYER)
    assertGame(PAPER, SCISSORS, SECOND_PLAYER)
}

And if we run the test suite—it still passes. At this point, we have eliminated the duplication. And spelling out the next business rule will be easy.

Watch:

@Test
fun `paper covers rock`() {
    assertGame(PAPER, ROCK, FIRST_PLAYER)
    assertGame(ROCK, PAPER, SECOND_PLAYER)
}

And to make this pass, we can just add second == ROCK to our if condition:

fun play(first: Throw, second: Throw): Winner {
    if (first == SCISSORS || second == ROCK) {
        return FIRST_PLAYER
    }

    return SECOND_PLAYER
}

While there is no duplication, the test still doesn’t read very well. At this point, we can play with a few options. For example, create a custom AssertJ assertion:

assertThat(SCISSORS).beats(PAPER)

That will expand to two assertGame calls. Another option is to use Kotlin’s infix extension functions that make English like sentences a piece of cake. Let’s implement a beats infix function for Throw enum:

private infix fun Throw.beats(other: Throw) {
    assertGame(this, other, FIRST_PLAYER)
    assertGame(other, this, SECOND_PLAYER)
}

And now we can replace double calls to assertGame in our tests:

@Test
fun `scissors cut paper`() {
    SCISSORS beats PAPER
}

@Test
fun `paper covers rock`() {
    PAPER beats ROCK
}

Again, we’re running into the same problem: the code itself already telling us in English what the test is about so we can merge these tests into one:

@Test
fun `game rules`() {
    SCISSORS beats PAPER
    PAPER beats ROCK
}

Adding the final pair is a no-brainer now:

Describing the final rule for the win condition (Red)

@Test
fun `game rules`() {
    SCISSORS beats PAPER
    PAPER beats ROCK
    ROCK beats SCISSORS
}

Now if we run the test—it fails because:

org.junit.ComparisonFailure: 
Expected: FIRST_PLAYER
Actual: SECOND_PLAYER

    at rps.GameTest.assertGame(GameTest.kt:28)
    at rps.GameTest.beats(GameTest.kt:19)
    at rps.GameTest.game rules(GameTest.kt:15)

Oh. I’m not sure. Of course, following the stacktrace, I could figure this out, but it is not immediately apparent.

This test failure sucks. So this is the other side of the trade-off of the abstraction in the test. You also have to make the failure more readable by giving more context in the failure message:

// inside of assertGame function:
assertThat(actual)
        .withFailMessage("""
            For a game $first vs. $second, the winner should be $expected

            Expected: $expected
            Actual: $actual
        """.trimIndent())
        .isEqualTo(expected)

Let’s rerun the test suite and take a look at the failure message:

java.lang.AssertionError: For a game ROCK vs. SCISSORS, the winner should be FIRST_PLAYER

Expected: FIRST_PLAYER
Actual: SECOND_PLAYER

    at rps.GameTest.assertGame(GameTest.kt:35)
    at rps.GameTest.beats(GameTest.kt:19)
    at rps.GameTest.game rules(GameTest.kt:15)

Oh, this is much better!

Enhancing our if statement to match the game rules (Green)

To make this assertion pass, we need to add a condition when the ROCK is the first throw:

if (first == SCISSORS ||
        second == ROCK ||
        first == ROCK) {
    return FIRST_PLAYER
}

That fails with “For a game ROCK vs. PAPER, the winner should be SECOND_PLAYER.” Let’s handle this case by clarifying that not only ROCK should be the first throw, but also SCISSORS has to be the second one:

if (first == SCISSORS ||
        second == ROCK ||
        first == ROCK && second == SCISSORS) {
    return FIRST_PLAYER
}

If we follow all these failures one by one, eventually we arrive at the following if statement that is absolutely symmetrical to the test:

Great! Remember, the winning conditions do not describe the full set of game rules. We also need to handle ties:

Testing for the tie (Red)

Let’s follow the same pattern and add the helper function for the “tie” rule:

private infix fun Throw.tiesWith(other: Throw) {
    assertGame(this, other, TIE)
    assertGame(other, this, TIE)
}

And let’s write all the rules for it (skipping a few cycles of TDD here to keep it short):

@Test
fun `game rules`() {
    SCISSORS beats PAPER
    PAPER beats ROCK
    ROCK beats SCISSORS

    SCISSORS tiesWith SCISSORS
    PAPER tiesWith PAPER
    ROCK tiesWith ROCK
}

Making the code work for the “tie” rules (Green)

It seems like the simplest solution for all three “tie” assertions would be to compare first throw with the second one, and see if they are equal:

fun play(first: Throw, second: Throw): Winner {
    if (first == second) {
        return TIE
    }

    if (first == SCISSORS && second == PAPER ||
            first == PAPER && second == ROCK ||
            first == ROCK && second == SCISSORS) {
        return FIRST_PLAYER
    }

    return SECOND_PLAYER
}

If we run our tests now, they’ll all pass. Fantastic!

Going overboard with the DSL (Refactor?!)

If you want to, you can go even further and introduce custom verbs for each of the Throw types. I wouldn’t recommend this for pragmatic reasons—it’s an overkill and will make people ask WTF at the rate of 20/s.

You can do it, and it is quite simple. Just introduce aliases for the beats function. Let’s do it for the SCISSORS cut PAPER only:

private infix fun Throw.cut(other: Throw) = beats(other)

// usage:
SCISSORS cut PAPER

For most of the involved domains, you might want to have custom verbs like that. I yet to see one though.

Thanks!

Thank you so much for reading. I hope you learned something new. To make me entirely happy, share this article on social media with your friends and colleagues.

Where do you see yourself using this in your test suite? Leave the comment below!

How to fix Gradle 4.4/4.7 + JDK 10 issue on IntelliJ IDEA 2018.1.x

Shares

Hi there, a few days ago I tried to build a Kotlin project using the latest OpenJDK 10, and it turned out that there is a problem with IntelliJ IDEA 2018.1.4 running an older version of Gradle. This article contains a step-by-step guide on how to fix this.

More about the issue here. The fix is already in the master branch and releases in one of the next versions of IntelliJ, but if you need the solution right now, read on.

This fix is temporary, and you should update to the newest version of IntelliJ as soon as it is released.

Step-by-step Guide

  1. If you already have a project created, great! Otherwise, create the project in IntelliJ using Gradle project template (be it Kotlin, Java, or something else).

    After you are done creating the project, you’ll see an error message that Gradle 4.4 can’t detect the version of your JDK:

    Could not determine Java version using executable /.../.../
    Could not determine Java version using executable /.../.../

    That is fine, move on to the next step.

  2. Install any recent version of Gradle on your system using the official instructions

    You may skip this step if you already have Gradle installed and it is available as a terminal/command-line binary gradle on your PATH.

  3. In the terminal, switch to your project directory

    cd ~/path/to/your/project/

  4. Generate Gradle Wrapper script with the version set to 4.7

    gradle wrapper --gradle-version 4.7

    This command is going to download Gradle 4.7, create scripts gradlew and gradlew.bat for your current project. From this point, you should use these scripts in the command-line.

    Next step, is to make sure that IntelliJ IDEA uses those, instead of built-in Gradle distribution.

  5. Open IDEA preferences and type gradle in the search field:

    Search for “gradle” and select “Build, Execution, Deployment / Build Tools / Gradle”
    Search for “gradle” and select “Build, Execution, Deployment / Build Tools / Gradle”

  6. Select Build, Execution, Deployment -> Build Tools -> Gradle in the sidebar like in the picture above.

  7. In this section of the settings select Use gradle ‘wrapper’ task configuration:

    Select “Use gradle ‘wrapper’ task configuration”
    Select “Use gradle ‘wrapper’ task configuration”

  8. Press the OK button.

  9. The project should be re-built (re-synced) automatically and you should see that it is downloading the Gradle 4.7:

    Gradle 4.7 version is being downloaded during Gradle Sync
    Gradle 4.7 version is being downloaded during Gradle Sync

Downsides of this temporary fix

This fix introduces another (bearable) problem:

Every time you change your Gradle configuration IntelliJ IDEA downloads Gradle 4.7 again.

Gladly, it doesn’t do that when running tests and building your software.

Thank you

Thank you for reading. Make sure to share this guide with anyone who wants to use Gradle 4.7 with IntelliJ IDEA 2018.1.x.

Clean Architecture For Android

Shares

NOTE: this article is still under construction. If you want to learn more these links will be beneficial to read:

This will soon become an article on adapting these concepts to Kotlin on Android.

Enjoy the reading!

Human-Oriented Approach

Shares

So you’ve got a feature to implement. Something like “James wants to enter expense?” Who is James anyways?

Persona

James is our Persona. It is the user of our application.

James is probably an employee of some company who is on a trip to the customer site. James wants to enter expenses so that the company can keep track of costs, reimburse them to James, and charge what is appropriate to the client.

When developing user-facing applications, it is a good idea to have a Persona like that in mind. And for every feature (and everything we program) we should think carefully what kind of value James wants to get here and why.

Since we already started talking about users and their personas, let’s go all out and write a User Story.

User Story

User Story captures the following aspects:

  • Who is the Persona?
  • What feature they want or need?
  • Why is that functionality valuable for them?
  • How can a tester verify that the feature works? (Acceptance Criteria)

Let me give you a template for the first three questions:

As {Persona},
I want/need to {my desire or need},
So that I {get the value}.

That template is not necessary. It is possible to have higher quality user stories when they don’t follow any template. On the other hand, it is hard to write a user story if you never did that, so template helps a lot in the beginning.

So if you know what you are doing, feel free to skip user story template and write it as you like.

If you are still a beginner with user stories, I highly suggest the use of such template. As you get more proficient with it, you should experiment with the template more.

The User Story for the current feature seems to be:

James wants to enter an expense:

As James,
I want to be able to enter an expense,
So that I can be reimbursed,
And my company can properly bill expenses to the client.

Before you rush to implement that, you’ll want to know when are you done with the feature. For that, you’ll need to write an Acceptance Criteria.

Acceptance Criteria

Acceptance Criteria are instructions for the tester (or automation framework) to verify that our User Story is implemented correctly.

When I say tester, I mean: the person who is wearing a hat of a tester. It could be an individual member of the team. Or it could be a developer, you. It could, as well, be your Product Owner, Project Manager or Product Manager. Finally, it could be your customer or client.

Within the boundaries of this tutorial, this person will be you.

They are usually written using Given/When/Then template:

Given <pre condition>
When <user’s action>
Then <expected outcome>

If you need multiple Givens, Whens or Thens:

Given <pre condition 1>
And <pre condition 2>
...
And <pre condition N>

When <user’s action 1>
And <user’s action 2>
...
And <user’s action N>

Then <expected outcome 1>
And <expected outcome 2>
...
And <expected outcome N>

The precondition is the description of what state the system and the user should be in before testing this feature.

User’s action is, well, just that: how the user interacts with the program.

The expected outcome is how we expect the system to behave after said user interaction given the provided preconditions. It is mostly the user-observed behavior.

Sometimes, though, we need to capture expected outcome as non-observed behavior. To write the story in such format, we need to involve the second persona, who has access to system internals.

In the context of our business domain, it could James’ manager, Kate, who wants to see the expense reports, accept and reject them. They are going to do that in some administrative area of the software, to which James does not have access.

Most often, though, it is possible to extract the whole other user story for that persona with its acceptance criteria. That is what we should do in that case.

OK. Enough theory. Let’s write acceptance criteria for the user story:

Example User Story

As James,
I want to be able to enter an expense,
So that I can be reimbursed,
And my company can properly bill expenses to the client.

## Acceptance Criteria

Given I am on the “Home” screen
When I tap on “Add Expense” button
Then I see the form with the fields:

- Date (defaults to today for convenience)
- Amount
- Currency drop down (single option for now – Euro)
- Needs reimbursement (check box)
- Client-related (check box)
- Comment

When I enter all the data
And I tap on “Save” button
Then I see the “Expense Details” screen
And I see all the data I’ve entered

Now, that you have the acceptance criteria, you should be able to translate it to the acceptance test for the business domain of your application.

Thanks

Thank you so much for reading this article. This post is a part of much more significant project iwillteachyoukotlin.com in which you are going to learn to program in Kotlin in general, and if you wish in the context of Android or/and Web development.

Android Studio Hotkey Map (for Ultimate Tutorial)

Shares

Download the hotkey map specifically designed for the Android Ultimate Tutorial:

Enjoy!