Pull to refresh

Comments 13

Ещё одна вредная для Kotlin статья, так как вы не показали почти ничего специфичного кроме data class-ов. Вот если бы DSL какой-нибудь изобразили — это было бы полезно.

Ок, спасибо! В следующий раз будем иметь в виду.

Мы используем котли, потому что котлин, котлин, котлин, он такой как котлин)

Если в DTO классах java сделать публичные поля, то не то же самое получится даже по объему кода?
DTO в Kotlin не только проще объявлять, там также много дополнительной функциональности, которая используется у нас в проекте.
С Kotlin мы существенно сокращаем количество кода, а значит, облегчаем жизнь и «писателям», и его «читателям». Класс ApplicationDTO уже содержит конструктор и некоторые другие методы (hashCode(), copy() и др.)

Было бы интересно сравнить Java + Lombok (который также здорово убирает бойлерплейт-код) vs Kotlin.
Сравнить можно :) Но Lombok решает только узкий круг проблем (более лаконичные ДТО в данном случае), а Kotlin предлагает решение комплексно.
Если вы захотите использовать Lombok будьте внимательны, потому что добавление новых зависимостей – это всегда риски (на эту тему вы можете посмотреть видео Сергея Немчинского www.youtube.com/watch?v=tkoFx3fDwz8.

Если уж говорить про Kotlin, то тестовый класс можно переписать как:


Пример теста
class ApplicationTests : FreeSpec() {

    init {
        val DEFAULT_LOGIN = "consLogin"
        val ERROR_CODE = "3"
        val BASE_URI = "https://base_url.com"

        // лучше вынести в отдельный класс, однако мы пытаемся просто продемонстрировать возможность
        val spec = RequestSpecBuilder()
                    .setContentType(ContentType.JSON)
                    .setBaseUri(BASE_URI)
                    .addFilter(ResponseLoggingFilter())
                    .addFilter(RequestLoggingFilter())
                    .build()

        "should reject incorrect login" {
            // Given
            val incorrectLogin = DEFAULT_LOGIN + "INCORRECT";

            // When 
            // в строках внизу происходит и выполнение запроса, и часть проверки результата, я бы разделил chain
            val actualResult = given()
                .spec(spec)
                .body(ApplicationDTO)
                .`when`()
                .post(endpointApplication(incorrectLogin)
                .then()
                .statusCode(400)
                .extract().`as`(ErrorDTO::class.java)

            // Then
            // Я бы сравнил класс целиком, тогда еще вывод красивый будет
            actualResult.error_message should haveSubstring("Указан неверный логин или пароль")
            actualResult.error_code shouldBe ERROR_CODE 
        }
    }
}

На самом деле тесты можно заталкивать в иерархии любой вложенности, так что написание параметризованных тестов немного упрощается. Ну и заодно внутрь можно заталкивать suspend функции, так что не надо писать runBlocking.


Пример теста
class SomeTest: FreeSpec() { // в будущем лучше создать свой класс и наследовать тесты от него
    init {
        "should correspond with cases from set 1" - { // если после строчки написать дефис, то тесты внутри будут под этой иерархией
            createTestCases1().forEach { testCase -> 
                " for ${testCase.name}" {
                    ...
                }
            }
        }

        "should correspond with cases from set 2" - {
            createTestCases2().forEach { testCase -> 
                " for ${testCase.name}" {
                    ...
                }
            }
        }
    }

    private fun createTestCases1(): List<TestCase1> { ... }

    private fun createTestCases2(): List<TestCase2> { ... }
}

Есть как минимум две библиотеки для тестов, которые позволяют так делать: Kotest & Spek.

Спасибо за комментарий. Рассмотрим и такой подход для усовершенствования тестов.
Добавлю свои 5 копеек про Kotest. У него ещё классная возможность есть — фабрики тестов
Создание фабрики:
fun <T> indexedSeqTests(name: String, empty: IndexedSeq<T>) = wordSpec {
   name should {
      "increase size as elements are added" {
         empty.size() shouldBe 0
         val plus1 = empty.add(1)
         plus1.size() shouldBe 1
         val plus2 = plus2.add(2)
         plus2.size() shouldBe 2
      }
      "contain an element after it is added" {
         empty.contains(1) shouldBe false
         empty.add(1).contains(1) shouldBe true
         empty.add(1).contains(2) shouldBe false
      }
   }
})

И её использование:
class IndexedSeqTestSuite : WordSpec({
   include(indexedSeqTests("vector"), Vector())
   include(indexedSeqTests("list"), List()))
})
Спасибо за комментарий. Рассмотрим и такой подход для усовершенствования тестов.
Спасибо за актуальное замечание. Исправили.
Sign up to leave a comment.