The <https://github.com/bnorm/kotlin-power-assert>...
# compiler
r
The https://github.com/bnorm/kotlin-power-assert plugin mentioned above made me wonder if anyone's gone as far as implementing Spock's
then
blocks in a compiler plugin? I still really miss the sheer simplicity of the syntax of Spock's tests.
👍 2
c
can you give an example of how that looks and what you like about it? (and why it needs a compiler plugin)
r
Here's an example:
Copy code
class EmailSpec extends Specification {

    EmailService emailService = EmailService()
    SmtpServer smtpServer = SmtpServer()

    def 'can send an email'() {

        given:
            def message = EmailMessage(
                emailAddress,
                emailSubject,
                emailBody
            )

        when:
            emailService.send(message)

        then:
            smtpServer.messages == [message]

        where:
            emailAddress = "<mailto:jane@example.com|jane@example.com>"
            emailSubject = "Hello World!"
            emailBody = "Please to meet you"

    }
}
Things I like: • No assertion noise in the
then
block - it's just one or more boolean expressions. I prefer this to
shouldBe
or
mustBe
or
is
or whatever, it's so clean, and because of power assertion you can reuse lots of stdlib boolean methods / functions and still get all the feedback you need on fail. • Moving data setup to the end of the test. Keeps the intent much, much clearer. • This is minor, but
given
,
when
,
then
being labels makes them "feel" much more part of the structure of the test than just comments, but without the noise of them being brace or parenthesis delimited blocks. You can get closeish in out of the box Kotlin with a bit of work, but it's never quite as clean and so never quite as readable.
j
I always found it kinda optimistic to expect that tests can be expressed this way. I often have tests cases that have a sequence of things happening, and I want to assert along the way: https://github.com/joffrey-bion/krossbow/blob/main/krossbow-stomp-core/src/commonT[…]tlin/org/hildan/krossbow/stomp/StompSessionSubscriptionsTest.kt
Also, how do you specify a failure message for those asserted boolean expressions?
r
I test drove my code using Spock as a professional dev for years. At least 5 of them.
You don't need to, that's the beauty of power assert.
j
So how would you write the test I linked above?
You don't need to, that's the beauty of power assert.
So how can I see on CI the reason for the failure as explained in those assertions? I mean the why of the assert within the failure message
Would you copy the same code into 4-5 different tests, and make one assertion per test?
r
You can have multiple
when
&
then
blocks in a Spock test if you want to assert and then continue.
👌 1
j
Then I don't quite see the point in breaking the sections down this way 🤔 remove all the given-when-then-where keywords and you have a "classic" unit test, apart from the fact that the variable values are defined at the end
r
I put
given
,
when
and
then
in my tests anyway as comments. Any test should have that format (optional declaration of initial state, input, expectation), and separating them clearly helps with both reading and thinking through exactly what it is you are testing. As I said in my "Things I like" comment above making them labels rather than comments makes it easier to read in my opinion, though this would be less so with Kotlin's
label@
syntax.
then
has semantic meaning because it changes boolean expressions into assertions.
s
I think even cooler would be to have datatables
or something similar
that is the only reason for me to use Spock (it's much slower than junit)
(there's also a parameterized test runner for JUnit 4, as well as a few other experimental data-driven test runners, but the JUnit5 approach works better IMO)
c
I’m not a huge fan of given/when/then, but parametrized tests are pretty easy with my test runner https://github.com/failgood/failgood (see the last block in “how it looks like”
s
@ephemient yes, junit5 can. but they are not as good as spock. that's the whole point of spock. junit4 could do this as well (keep that in mind) https://www.testwithspring.com/lesson/writing-parameterized-tests-with-junit-4/ why not use junit4? people prefer spock for this, some went and use junit5
c
and instead of given/when/then you can just replace
given
and
when
with describe, and then with
it
l
Writing a compiler plugin has indeed steep learning curve, but once you get used to it, it will even fun to add extra power to kotlin. Things needed here: • That comparison would be passed to bare IrExpression to Backend IR, so replace it with
someAssertFunction(original expr)
. For example
then { a == b }
would become
then { assert(a == b) }
. (here,
then {}
is a simple marker.) • Comparing some well-known types (like String, Int) causes 'unused expression', so need to suppress that warning through
DiagnosticSuppressor
That's all, this one seems fairy easy to implement.
s
@lhwdev how about spocks data tables? is it doable somehow?
I know kotlin is not as flexible as groovy, but something similar?
l
(In fact I don't know spocks what is that)
e
that syntax is definitely not doable in Kotlin
but you can pretty much get it with JUnit 5's
@CsvSource
(whose delimiters can be changed to
|
if you like)
and yes, I am aware of JUnit 4's parameterized tests and also popper/theories, but neither is as simple to use nor as flexible as JUnit 5
l
I would like using plain kotlin syntax, like
Copy code
where {
  item(1, 2, 3)
  item(4, 5, 6)
}
Compiler plugins cannot do that. Extending kotlin itself for this is overkill.
c
yes thats exactly how you would do it in a kotlin test dsl like kotest or failgood
s
@ephemient at work we are moving away from junit5 into spock. the syntax is much different
junit5 is just not as east to write and read as spock. here you have everything in one method. that is the main feature.
c
interesting, I’m a bit suprised that people use groovy test runners with kotlin.
s
I had no issue with it, the only thing is that sometimes I need to add @JvmStatic
s
Other than putting data tables in a text block, what's the real "advantage" to the spock way ? Is it simply that - it's perceived to be cleaner ?
s
@sam cleaner, easier to write parameter tests. keep in mind that groovy is very powerful language. you can implement interfaces at runtime, access object as map ( obj["field"]). also asserts are super cool: obj.field == 1 obj.field2 == "2" or multiple asserts verifyAll { obj.field == 1 obj.field2 == "2" } or even use with with(obj) verifyAll { field == 1 field2 == "2" } types are not mandatory. you can do: def VALUE = 1 etc.
or even mocks are done something like obj.method(_) << "mockedValue"
there is also this cool feature (great for testing) -> https://www.logicbig.com/tutorials/misc/groovy/spread-operator.html
also creating list this way in tests is amazing -> def lists = [[1], [10, 20, 30], [6, 8]]
and last, but not least this feature of showing exceptions
Copy code
Condition not satisfied:

stack.size() == 2
|     |      |
|     1      false
[push me]
r
That's power assert - it's what prompted me to ask in the OP, because it looks like that can be done with a Kotlin compiler.
s
powerassert is a copy of that feature, it was in spock for many years (5 at least)
with spock you don't need the plugin
r
Yes, I know. I used groovy & spock for a decade.
s
there is only 1 issue with spock. I feel its like 5 times slower to execute
@Rob Elliot is spread operator doable as kotlin plugin?
r
No idea
l
That spread operator would be definitely inavailable in kotlin, however I would prefer
.map {}
even more than Groovy; it is more readable, intuitive to me.
s
@lhwdev I don't use that feature as much, but if I have it - I prefer it. it just hard to remember it exists, since in tests is not needed often
r
I'm less enthusiastic than server about the features of groovy as a language - I'd rather use kotlin, including for my tests. It's the specifically spock syntax I value - the ability to put a
where:
table inside and at the bottom of the test function, but still reference its variables in the test name and in the test - that makes the test cleaner to read. It goes straight from the name to expressing what matters about the test, without a bunch of setup syntax above it.
c
@Rob Elliot is spread operator doable as kotlin plugin?
no. kotlin plugins are not for introducing new syntax. power assert is a good example. it just improves the error message that a normal assert would produce. the source code would still compile without the plugin.
r
c
r
I'd rather use comments, that's way too many quotes and backticks and braces and nesting, which for me are precisely the things that get in the way of understanding the intent of the test as fast as possible.
c
but it all has its downsides. for me the 10 different testing styles were one reason to write my own testing framework instead of contributing to kotest. and while given/when/then certainly seems nice I’m hesitant to add it to failgood, because for me its only syntactic sugar. (i may add it at some other time when i know more about it or worked with people who use it)
s
@Rob Elliot thanks for that, I didn't know kotest can do that. I always assumed it was just like JUnit - my mistake.
@amorangi how is your test framework different? what features it has that are not in those frameworks? do you have some custom extension methods?
c
I think you meant to ping me. my test framework is just a simple framework with describe/it syntax, full context isolation and parallel test execution. see here https://github.com/failgood/failgood it also does not contain an assertion library, I use #strikt for that.
s
thanks,I will look into it