I hope you don't mind me asking a "heretic" questi...
# testballoon
s
I hope you don't mind me asking a "heretic" question, but out of curiosity, as TestBalloon seems to care about elegance, I've always wondered by it chose to declare test suites via property delegates instead of classes. Because whenever I read something like
val MyFirstTestSuite by testSuite {}
my reflex is to remove this unused variable 😬
o
Full answer will follow (AFK now), but just for my understanding: There would be no reflex to remove the unused class?
s
Good, question... interestingly no. I guess that's because at least I'm used to seeing "unused" classes as part of library APIs, or in general people are more used to test being classes discovered by classpath scanning. But seeing unused properties is much more unusual IMHO.
e
If you plug IDE plugin that will show that variable is "used"
s
Correct. For me it's not so much about IDE highlighting / inspection hints, but the syntax in general. To me it's just a very exotic choice I have to make myself comfortable with. Sure, very subjective, and I'm just curious about the design choice.
👍🏼 1
o
Actually, class declarations for top-level test suites were supported initially, mainly for historical reasons. They were deprecated in version 0.7.0 and removed in 0.8.0. Why? • They are more verbose (and consume more vertical space with auto-formatting by Ktlint) without providing additional value. ◦ Class using a constructor lambda:
Copy code
class SimpleTestSuite :
    TestSuite(
        {
            test("string length") {
                assertEquals(8, "Test me!".length)
            }
        }
    )
◦ Class using an
init
block:
Copy code
class SimpleTestSuite : TestSuite {
    init {
        test("string length") {
            assertEquals(8, "Test me!".length)
        }
    }
}
◦ Property:
Copy code
val SimpleTestSuite by testSuite {
    test("string length") {
        assertEquals(8, "Test me!".length)
    }
}
• The property delegate makes defining top-level test suites consistent with lower-level test suites. The syntax is almost identical, the main difference being that the suite name is auto-generated by default. TL;DR: Conciseness, consistency, comprehensibility.
s
There's (with Kotest) a shorthand-syntax to avoid the
init
-block:
Copy code
class CryptoHelperTest : FunSpec({
    test("encryptSessionToken()") {
        // ...
    }
})
which arguably is not less readable than your last example. But I get your point, and in the end it boils down to a matter of taste.
o
That was my first example: constructor lambda.
s
Sorry, I didn't recognize it due to the "verbose" formatting 😅
o
Yeah, Slack is less than ideal for this kind of stuff.
Also out of curiosity, imagine a world where you have learned it the other way around: There is this pattern of specifying a test suite:
Copy code
testSuite("weather service tests") {
    // ...
}
and this pattern for a top-level test suite:
Copy code
val WeatherServiceTests by testSuite {
    // ...
}
And you know, it's the same thing, just a function invocation. Which means that instead of using the above functions, you could always provide your own custom ones. If this were the world you were raised in, would you ever consider introducing classes instead?
s
Of course, very hard to answer by pretending to be unbiased. From a "cognitive load" perspective I guess there is not much difference for the human eye / brain to parse
val SimpleTestSuite by testSuite {
vs.
class SimpleTestSuite : FunSpec({
, it's about the same length, where the one has more words, and the other more special chars. But if you also consider semantics, and you know that
val ... by
is actually instantiating something (on use) whereas
class
just defines a test class, the former could still feel a bit odd. But we're getting very theoretical now 😉
o
that
val ... by
is actually instantiating something (on use)
Actually, using your terminology, it defines just the property. Like the class. Invoking the property's getter has the same effect as instantiating the class.
s
Agreed. It's just arguably more unusual to come to that conclusion.