Choosing an assertion library for a Kotlin project

In one of the old projects, asserts from JUnit, kotlin.test and AssertJ were piled up. This was not his only problem: it was generally written as a letter from Uncle Fyodor, and there was no time to stop and bring it to a single form. And now this time has come.



The article will contain a mini-research about which asserts are better according to subjective criteria. At first I wanted to do something simple: throw in a set of tests in order to quickly rivet options with copy-paste. Then he highlighted the general test data, automated some of the checks, and how everything went ... As a result, we got a small Rosetta stone and this article can be useful to you in order to choose a library of asserts that suits your realities.



I will make a reservation right away that the article will not compare testing frameworks, testing approaches, and some tricky approaches to data validation. We will talk about simple asserts.





If you are too lazy to read boring reasoning, the history of my ordeals and other details, then you can go directly to the comparison results .



A bit of background



Scala, β€” ScalaTest. , - , - .



Kotlin, - , , Kotest ( , ).





, , β€” . :



  1. Kotlin IntelliJ Idea. Scala- β€” . , ScalaTest , . IntelliJ <Click to see difference>, . , , IntelliJ Idea β€” Kotlin- - , ?

  2. . 1 != 2 , , "expected" "actual". " 100 , , ", , "… , , , ". , ? , β€” , , - .
  3. . assertEquals(expected, actual) β€” , . , β€” /, " , , " , contains, includes. β€” , .
  4. . - assertThat("Friendship").contains("end").
  5. . - JUnit4, , ExpectedException @Rule.
  6. () .
  7. .
  8. . β€” , . , : , , generic-, . -: assertThat(generic<Boolean>(input)).isEqualTo(true). <Boolean> . .
  9. , . . ? β€” , , . - , equals. - .


, , , , , . , β€” , . , -, QA-.





, 5 , , β€” .



:



  1. ScalaTest β€” .
  2. JUnit 5 β€” Java-.
  3. kotlin.test β€” multiplatform . β€” JUnit, .
  4. AssertJ β€” . FestAssert, - .
  5. Kotest β€” KotlinTest, kotlin.test. , ScalaTest. 1-22 β€” scala.
  6. Truth β€” . , AssertJ.
  7. Hamrest Β­β€” . valid4j.
  8. Strikt β€” AssertJ .
  9. Kluent β€” , JUnit ( β€” kotlin.test), Kotest. β€” , .
  10. Atrium β€” , AssertJ, . β€” ( maven/gradle).
  11. Expekt β€” Chai.js. : β€” 4 .
  12. AssertK β€” AssertJ, AssertK ( ).
  13. HamKrest β€” Hamrest, HamKrest ( Hamcrest ).


β€” , -.





80 , , , . - , .



, , 1 β€” 1 . , , - , " , " "".



, - , JUnit , , , , , . ScalaTest, : , , β€” . : ? , :). / , , .



: listOf(1,2,3)? β€” -, β€” . , , : , . , .



type erasure. Reified inline-, .



assertThrows<T>{...}


, reified :



assertThrows(expectedClass){...}


. , kotlin.test Asserter: , . , ?:)



GitHub. ScalaTest , .





: 0 β€” , 0.5 β€” , 1 β€” . β€” 9 .



-, , . . , . , - , " " , " " . .



Kotest Β± Β± + + + + + - 6.0
Kluent Β± Β± + + + + + - 6.0
AssertJ Β± + Β± + Β± + + Β± 6.0
Truth Β± + + + - + + - 5.5
Strikt Β± Β± Β± + + + + - 5.5
ScalaTest Β± Β± Β± + + + + - 5.5
HamKrest Β± - Β± + + Β± + - 5.5
AssertK Β± Β± Β± + Β± + + - 5.0
Atrium Β± Β± Β± + + Β± + - 5.0
Hamrest Β± Β± Β± + - Β± + - 5.0
JUnit + + - Β± + - Β± - 4.5
kotlin.test + Β± - - + - - - 3.5
Expekt Β± Β± - + - Β± + - 3.5


:



Kotest
  • To connect only asserts, you need to dig deeper. In my opinion, it's hard to understand this straight away.
  • , reified, : .
  • : . .
  • : <Click to see difference> .
  • : .
  • : , .


Kluent
  • "hello".shouldBeEqualTo("hello"), "hello" `should be equal to` "hello". DSL .
  • :

    invoking { block() } shouldThrow expectedClass.kotlin withMessage expectedMessage
  • , , . Expected Iterable to contain none of "[1, 3]" β€” , Iterable .
  • : <Click to see difference> .
  • : .


AssertJ
  • β€” … , containsExactly, β€” hasSameElementsAs, β€” .usingRecursiveComparison().isEqualTo.



  • : <Click to see difference> .



  • : - , . , , .



  • : .usingRecursiveComparison(), . : , , . , , ,



    assertThat(actual)
        .usingRecursiveComparison()
        .isNotEqualTo(unexpected)


    : .



  • : . , DSL.





Truth
  • , .
  • : , , assertThrows JUnit5. , JUnit , ?
  • : , , : containsAtLeastElementsIn. , assertThat(actual).isEqualTo(expected).
  • : <Click to see difference> .
  • : .
  • : , .
  • " ":

    expected: 1
    but was : 2
    at asserts.truth.TruthAsserts.simpleAssert(TruthAsserts.kt:10)
    at common.FailedAssertsTestBase.simple assert should have descriptive message(FailedAssertsTestBase.kt:20)
    at [[Reflective call: 4 frames collapsed (https://goo.gl/aH3UyP)]].(:0)
    at [[Testing framework: 27 frames collapsed (https://goo.gl/aH3UyP)]].(:0)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at [[Testing framework: 9 frames collapsed (https://goo.gl/aH3UyP)]].(:0)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at [[Testing framework: 9 frames collapsed (https://goo.gl/aH3UyP)]].(:0)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at [[Testing framework: 17 frames collapsed (https://goo.gl/aH3UyP)]].(:0)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
    ...


Strikt
  • , reified, : .



  • : expectThat(haystack).not().contains(needle), expectThat(collection).doesNotContain(items).



  • : contentEquals. : expectThat(actual).not().contentEquals(unexpected). , , Array<T> Strikt - . β€” containsExactly, β€” containsExactlyInAnyOrder.



  • : . , . :



    val actual: Array<String> = arrayOf("1")
    val expected: Array<String> = arrayOf("2")
    expectThat(actual).contentEquals(expected)


    , contentEquals. , contentEquals :



    infix fun <T> Assertion.Builder<Array<out T>>.contentEquals(other: Array<out T>)


    -



    val actual: Array<out String> = arrayOf("1")
    val expected: Array<String> = arrayOf("2")
    expectThat(actual).contentEquals(expected)


  • : <Click to see difference>.



  • : , .



  • : .





ScalaTest
  • : .
  • : , . .
  • : DSL contains, contains include, theSameElementsAs.
  • : , .
  • : , .


HamKrest
  • , , . β€” , .
  • , Hamcrest, - : -.
  • β€” :

    assertThat( {
        block()
    }, throws(has(RuntimeException::message, equalTo(expectedMessage))))
  • : . - 3,5 . : assertThat(collection, allOf(items.map { hasElement(it) })).
  • .
  • : .
  • : <Click to see difference>.
  • β€” - :

    expected: a value that not contains 1 or contains 3
    but contains 1 or contains 3


AssertK
  • β€” AssertJ. ( , -).



  • : AssertJ assertThat(collection).containsAll(items), AssertK , containsAll vararg. , containsAll(1,2,3), . , , β€” . β€” . , containsOnly containsExactly.



  • : <Click to see difference>.



  • : - , , .



  • : .usingRecursiveComparison() .



  • β€” ( ), :



    expected to contain exactly:<[3, 4, 5]> but was:<[1, 2, 3]>
    at index:0 unexpected:<1>
    at index:1 unexpected:<2>
    at index:1 expected:<4>
    at index:2 expected:<5>


    ?



  • : .





Atrium
  • : fluent infix. assertThat(x).isEqualTo(y) x shouldBe y, , expect(x).toBe(y) expect(x) toBe y. , , "". - o: expect(x) contains o atLeast 1 butAtMost 2 value "hello". , , . infix- ( - ), Atrium fluent-.
  • : : notToBe, containsNot. . , : contains vararg, containsElementsOf , . , contains(1,2,3), . expect(collection).containsNot.elementsOf(items).
  • , toList.
  • , reified, : .
  • : .
  • : <Click to see difference>.
  • : ( , ), :

    expected that subject: [4, 2, 1]        (java.util.Arrays.ArrayList <938196491>)
    ◆ does not contain:
    ⚬ an entry which is: 1        (kotlin.Int <211381230>)
      βœ˜β€„β€„number of such entries: 1
           is: 0        (kotlin.Int <1934798916>)
        has at least one element: true
           is: true
  • : .


Hamcrest
  • : (



    assertThat(actual, `is`(not(unexpected)))




    assertThat(actual, not(unexpected))


    containsString vs contains vs hasItem vs hasItems. , : hasItems vararg, Set<T> T . , hasItems(1,2,3), .



    assertThat(collection, allOf(items.map { hasItem(it) }))


    :



    assertThat(collection, not(anyOf(items.map { hasItem(it) })))


  • hasItems, Β± "", , .



  • : .



  • : <Click to see difference>.



  • : .



  • : .





JUnit
  • : - assertEquals(expected, actual), : assertArrayEquals, assertIterableEquals ..
  • : , JUnit - , .
  • : assertLinesMatch(listOf(".*$needle.*"), listOf(haystack)) , .
  • : assertLinesMatch, , assertIterableEquals.
  • : , assertIterableEquals Map Set , .
  • : .


kotlin.test
  • . JUnit, . , .
  • , JUnit, :
  • .
  • assertIterableEquals, .
  • : JUnit' assertEquals, kotlin.test , .
  • : .


Expekt
  • expect(x).equal(y) x.should.equal(y), . , .
  • : contains(item) should.have.elements(items) should.contain.elements(items). containsAll. , : should.have.elements vararg. , should.have.elements(1,2,3), . any: .should.not.contain.any.elements.
  • : , .
  • .
  • .
  • : .
  • : .
  • : <Click to see difference>.




Kotest, Kluent AssertJ. , Kotlin , AssertJ ( ). , .



β€” , , AssertJ. , , .




All Articles