From PHP to Kotlin - Day 6 - Entities
Tags: kotlin, tutorial, beginners, programming
Published: 2022-01-28 15:58:27

---
series: From PHP to Kotlin
---

Entities are other thing. Are more complex and require more care.

| Specifications                                                                      |
| :--- |
| Mush have unique identifier
| Must have value objects
| Must be able to contain other entities
| Must be able to be constructed in different ways if necessary "named constructors"

In this case we're going to create the Mower Entity.

> The truth is that before starting this activity, the Mower entity was de first that was created, but it was quite anemic, since it needed other value objects like position or orientation to be created by composition and that's why I left it parked, since that didn't serve our purpose. 

What we need from our Mower is:

- That can be build
- That can turn left
- That can turn right
- That can move along
- That detects if it goes out of the limits of the map

Which leaves us with a test like this:

``` kotlin
private const val MOVE_LEFT: String = "L"
private const val MOVE_RIGHT: String = "R"
private const val MOVE_FORWARD: String = "F"

private const val X_POSITION: Int = 1
private const val Y_POSITION: Int = 1
private const val STEP: Int = 1

private const val NORTH_ORIENTATION: String = "N"
private const val WEST_ORIENTATION: String = "W"
private const val EAST_ORIENTATION: String = "E"
private const val SOUTH_ORIENTATION: String = "S"

internal class MowerTest {
    private lateinit var mower: Mower
    private val mowerId: MowerId = MowerId.build(UUID.randomUUID().toString())
    private lateinit var mowerPosition: MowerPosition

    @BeforeEach
    fun setUp() {
        mowerPosition = MowerPosition.build(
            XMowerPosition.build(X_POSITION),
            YMowerPosition.build(Y_POSITION),
            MowerOrientation.build(NORTH_ORIENTATION)
        )

        mower = Mower.build(mowerId, mowerPosition)
    }

    @Test
    fun `Should be build`() {
        val mower = Mower.build(mowerId, mowerPosition)

        assertThat(mower).isInstanceOf(Mower::class.java)
        assertThat(mower.mowerId).isEqualTo(mowerId)
        assertThat(mower.mowerPosition()).isEqualTo(mowerPosition)
    }

    @Test
    fun `Should be able to turn left`() {
        val expectedMowerOrientation = MowerOrientation.build(WEST_ORIENTATION)

        mower.move(MowerMovement.build(MOVE_LEFT))
        assertThat(mower.mowerPosition().orientation).isEqualTo(expectedMowerOrientation)
    }

    @Test
    fun `Should be able to turn right`() {
        val expectedMowerOrientation = MowerOrientation.build(EAST_ORIENTATION)

        mower.move(MowerMovement.build(MOVE_RIGHT))
        assertThat(mower.mowerPosition().orientation).isEqualTo(expectedMowerOrientation)
    }

    @Test
    fun `Should be able to move forward`() {
        mower.move(MowerMovement.build(MOVE_FORWARD))
        assertThat(mower.mowerPosition().xPosition.value).isEqualTo(X_POSITION)
        assertThat(mower.mowerPosition().yPosition.value).isEqualTo(Y_POSITION + STEP)
    }

    @ParameterizedTest
    @MethodSource("positionAndMovementProvider")
    fun `Should throw exception if goes out of bounds`(xPosition: Int, yPosition: Int, orientation: String) {
        val mowerPosition = MowerPosition.build(
            XMowerPosition.build(xPosition),
            YMowerPosition.build(yPosition),
            MowerOrientation.build(orientation)
        )

        val mower = Mower.build(mowerId, mowerPosition)

        assertThrows(InvalidMowerPositionException::class.java) {
            mower.move(MowerMovement.build(MOVE_FORWARD))
        }
    }

    companion object {
        @JvmStatic
        fun positionAndMovementProvider(): Stream<Arguments> {
            return Stream.of(
                Arguments.arguments(0, 0, SOUTH_ORIENTATION),
                Arguments.arguments(0, 0, WEST_ORIENTATION),
                Arguments.arguments(0, 5, NORTH_ORIENTATION),
                Arguments.arguments(5, 0, EAST_ORIENTATION)
            )
        }
    }
}
```

If all went well in the previous value objects, our Mower should look like this:

``` kotlin
import mower.mower.value_object.MowerId
import mower.mower.value_object.MowerMovement
import mower.mower.value_object.MowerPosition

class Mower private constructor(val mowerId: MowerId, private var mowerPosition: MowerPosition) {

    fun move(movement: MowerMovement) {
        mowerPosition = mowerPosition.move(movement)
    }

    companion object {
        @JvmStatic
        fun build(mowerId: MowerId, mowerPosition: MowerPosition): Mower {
            return Mower(mowerId, mowerPosition)
        }
    }

    fun mowerPosition(): MowerPosition {
        return mowerPosition
    }
}
```

All the logic has been distributed in the value objects in a natural way. Every piece fulfills its function. Te Mower sould only apply the movement that it passes to the MowrePosition object, which decides if it's a forward movement or a change of direction, and executes the necessary changes...

The Value objects **XMowerPosition** and **YMowerPosition** watch for negative positions in the coordinates. But there aren't able to know if they are outside the map in positive coordinates. The tests that we've created for Mower will fail in this specific field, so we must pass the map to it to validate that casuistry...

---

## Bonus test and context

The truth is that I'm quite a fan of giving a little explanation when the tests are executed. You never know when one is going to fail and the idea is that the person who comes after us has a bit of context as to why is failing.
See a test without context about values used it can be difficult. 

``` kotlin
...
    @ParameterizedTest(name = "{0}")
    @MethodSource("positionAndMovementProvider")
    fun `Should throw exception if goes out of bounds`(scenario: String, xPosition: Int, yPosition: Int, orientation: String) {
...
    }

    companion object {
        @JvmStatic
        fun positionAndMovementProvider(): Stream<Arguments> {
            return Stream.of(
                Arguments.arguments("Out of bounds by negative value movement axis Y", 5, 0, SOUTH_ORIENTATION),
                Arguments.arguments("Out of bounds by negative value movement axis X", 0, 5, WEST_ORIENTATION),
                Arguments.arguments("Out of bounds movement axis Y", 0, 5, NORTH_ORIENTATION),
                Arguments.arguments("Out of bounds movement axis X", 5, 0, EAST_ORIENTATION)
            )
        }
    }
```
It may seem a bit strange to add an argument that is not going to be used in the test.... but it makes sense when...

**We go from this**:


![Context in tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qatobm0zgma79rx4ggaa.png)

**Or this**:

![Context in tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxa6znanuhuv5hgot5fi.png)

**To this**:


![Context in tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmslnpgecpxecl3ufhgm.png)

Much clearer and more readable...

Although **JUnit gives us a warning** for not actually using the variable... it seems that it doesn't like our use of it.

![Context in tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j38hjjix26t5kc9ff7q8.png)

---

## Links and resources

[Repository Mower Kata](https://github.com/faparicior/mower-kata-kotlin)