Thread
#atrium
    Igor Akkerman

    Igor Akkerman

    2 years ago
    Hi, I'm new to Atrium (and Kotlin) so bear with me. I have a collection of objects and I want to make assertions on its content using some of the objects' properties. What I currently have is not readable. Is there a cleaner way?
    data class Member(val name: String, val interest: String, val favoriteNumber: Int)
    val members = listOf(Member("Me", "Kotlin", Random.nextInt()), Member("MyDog", "food", Random.nextInt()))
    expect(members)
            .containsExactly(
                    { feature { f(it::name) }.toBe("Me") ; feature { f(it::interest) }.toBe("Kotlin")},
                    { feature { f(it::name) }.toBe("MyDog") ; feature { f(it::interest) }.toBe("food")}
            )
    r

    robstoll

    2 years ago
    Hi Igor, in case you use this combination name/interest often, then I would suggest you add a corresponding assertion function (maybe you can come up with a better name):
    fun Expect<Member>.hasNameAndInterest(name: String, interest: String) =
        this.feature({ f(it::name) }, { toBe(name) })
            .feature({ f(it::interest) }) { toBe(interest) }
    then it can be rewritten to:
    expect(members).containsExactly(
        { hasNameAndInterest("Me", "Kotlin") },
        { hasNameAndInterest("MyDog", "food") }
    )
    In case
    toBe
    was only an example and you want to perform other assertions on the properties, e.g.
    startsWith("Kot")
    then you could provide a shortcut for the properties:
    val Expect<Member>.name
        get() = name(this).getExpectOfFeature()
    
    // allows to define an assertion group for the feature
    fun Expect<Member>.name(assertionCreator: Expect<String>.() -> Unit) =
        name(this).addToInitial(assertionCreator)
    
    private fun name(expect: Expect<Member>) = ExpectImpl.feature.property(expect, Member::name)
    Whether this boilerplate is worth it depends on how many times you use the property IMO. Then you could use it as follows:
    expect(members).containsExactly(
        { name.toBe("Me") },
        { name { startsWith("Ro"); endsWith("t") } }
    )
    @Igor Akkerman I hope that helps, otherwise let me know
    Igor Akkerman

    Igor Akkerman

    2 years ago
    Hi Robert, thanks a lot for your help. I'm really a fan of the flexibility of Atrium and the expressiveness of the assertion code and especially of the output in case of test failure. Might there be a way on the side of the library to make the latter version require less boilerplate, generalizing assertions on properties? From AssertJ, I remember it allowing to specify fields to do comparisons on. It was using the field names as Strings, though. I noticed a tiny irregularity: While the assertion works correctly, in entry 1, the output shows "name" as "Me", while the object has "MyDog". Could the assertion have cached the value in a wrong way?
    ch.tutteli.atrium.reporting.AtriumError: expect: [Member(name=Me, interest=Kotlin, favoriteNumber=1771517067), Member(name=MyDog, interest=food, favoriteNumber=-1574926733)]        (java.util.Arrays.ArrayList <1682681674>)
    ◆ contains only, in order: ✔️▶️ entry 0: Member(name=Me, interest=Kotlin, favoriteNumber=1771517067) (atriumtest.AtriumTest.Member <613298587>)  an entry which: » ▶️ name: "Me" <1446983876>  to be: "Me" <1446983876> » ▶️ interest: "Kotlin" <1042790962>  to be: "Kotlin" <1042790962> ✘ ▶️ entry 1: Member(name=MyDog, interest=food, favoriteNumber=-1574926733) (atriumtest.AtriumTest.Member <2130192211>)  an entry which: » ▶️ name: "Me" <1446983876>  to be: "MyDog" <990897274> » ▶️ interest: "Kotlin" <1042790962>  to be: "foodX" <539690370> ✔️▶️ size: 2 (kotlin.Int <593415583>)  to be: 2 (kotlin.Int <593415583>)
    r

    robstoll

    2 years ago
    do you mean simplifying
    feature { f(it::name) }
    ?
    or are you talking about something in the output?
    what would you like to see?
    ah... forget what I just wrote 😄 I missed the
    I noticed a tiny irregularity
    Igor Akkerman

    Igor Akkerman

    2 years ago
    You didn't miss it, I hit enter to quickly and edited the original message 😉
    r

    robstoll

    2 years ago
    ah ok
    Igor Akkerman

    Igor Akkerman

    2 years ago
    Yes, I meant simplifying
    feature { f(it::name) }
    r

    robstoll

    2 years ago
    the irregularity is actually a bug I have on my radar for a longer time (planned for 0.10.0 now, see roadmap https://github.com/robstoll/atrium#0100). You are the first noticing/reporting it though
    Atrium should not show the value at all in, it should be as follows:
    an entry which:
    >> name:
      - to be: "My Dog"
    Igor Akkerman

    Igor Akkerman

    2 years ago
    Actually, it's better if it shows the actual value alongside the expected value, so you see the diff immediately
    r

    robstoll

    2 years ago
    regarding simplifying
    feature
    it is mainly that complicated because Kotlin has unfortunately many bugs in the area of type inference and method overloading. See the list here https://github.com/robstoll/atrium#kotlin-bugs, would be nice if you can upvote some, so jetbrains invests a bit more time in this area
    you are right, it could be shown for
    in order only
    , it would not make sense for
    in any order
    .
    Igor Akkerman

    Igor Akkerman

    2 years ago
    True
    r

    robstoll

    2 years ago
    good input, I would have removed it for all functions, I'll keep that in mind
    Igor Akkerman

    Igor Akkerman

    2 years ago
    Great, thanks
    r

    robstoll

    2 years ago
    you could also write
    feature(Member::name)
    in case you find that simpler
    see also https://github.com/robstoll/atrium-roadmap/issues/21 maybe you can comment there regarding your use case
    Igor Akkerman

    Igor Akkerman

    2 years ago
    ah missed that one, that's perfectly readable/writable then
    awesome
    I believe
    feature(Member::name)
    is good enough, really. No idea if something like
    containsExactly({Member::name toBe "Me"; Member::interest toBe "Kotlin"})
    would be technically possible
    To me, comparing data classes by showing their toString() diff is perfectly fine