<Inspectors> have sort of eluded me.. My typical u...
# kotest
e
Inspectors have sort of eluded me.. My typical use-pattern when writing tests is to write my test and go
x should...
and look for a good assertion that does what I want it to, perhaps throwing in some word that fits. Would it make sense to just bake inspectors into the regular set of collector matchers and deprecate collection matchers which take a predicate in favour of ones that use matcher-blocks?
1
s
We probably could do some kind of unification yes, too many ways to do the same thing atm
e
Could be renamed something like this perhaps?
forAll
->
shouldAll
which asserts every element passes the assertions
forNone
->
shouldContainNone
which asserts no element passes
forOne
->
shouldContainSingle
which asserts only a single element passed
forAtMostOne
->
shouldContainAtMostOne
which asserts that either 0 or 1 elements pass
forAtLeastOne
->
shouldContainAtLeastOne
which asserts that 1 or more elements passed
forAtLeast(k)
->
shouldContainAtLeast(k)
which is a generalization that k or more elements passed
forAtMost(k)
->
shouldContainAtMost(k)
which is a generalization that k or fewer elements passed
forAny
which is an alias for forAtLeastOne ->
shouldContainAtLeastOne
forSome
->
shouldContainSome
which asserts that between 1 and n-1 elements passed. Ie, if NONE pass or ALL pass then we consider that a failure.
forExactly(k)
->
shouldContain(k)
which is a generalization that exactly k elements passed. This is the basis for the implementation of the other methods
s
I'm not sure we should use the word should since they're not actually matchers per se ?
e
hmm, not sure I understand the difference.. I sometimes do:
Copy code
subject.should {
  it.name shouldBe "karl"
  it.age shouldBeGreaterThan 25
}
In this case we enable
Copy code
val subjects = listOf(...)
subjects.shouldAll {
  it.age shouldBeInRange 18..65
  it.name shouldBeIn listOf("hans", "greta")
}
It feels like a natural analogue between single <-> collection assertions to me at least 🙂
s
hmmmm
subjects.shouldMatchAtMost(k) { }
or
subjects.forAtMost(k) { }
Is this a change that gains us much ?
e
hard to argue, could improve discoverability but not sure
s
Sometimes the fact something has been like it is for 5 years is enough of a reason to keep it the way it is, unless there's a discernible reason to change it.
e
s
agreed
Probably should stick to a format, or offer both
e
I'll start a PR and explore it a bit, and if it doesn't sit right we can scrap it 🙂
s
Yeah ok nice 🙂
w
Personally I find myself wanting to write
someList shouldHaveSingleElement { it.label == "expected" }
, that is piggyback on the existing collection matchers, but pass another matcher as the expectation
s
so like
someList.shouldHaveSingleElement(::matcher) ?
w
Yep
s
isn't that just the same as
someList.forOne { a shouldDoSomething b }
w
I also didn’t know until this week that inspectors exist 😄 But wait, it’s not actually what I want, I see there’s
Collection<T>.shouldHaveSingleElement(p: (T) -> Boolean)
already. What I miss most often is a way to compare two lists with custom comparator
s
example of how you would use that if it existed ?
w
so like
Copy code
data class Foo(val str: String, val callback: () -> Unit)
callback is usually different so I want to compare by
str
only. Right now we have
Copy code
fun List<Foo>.toSnapshot() = "$str"

fooList.toSnapshot() shouldBe expectedFooList.toSnapshot()
I don’t know what I’d like, but something like
Copy code
fooList.something(expectedList) { actual, expected -> actual.str == expected.str }
but I’m pretty tired and I don’t know if it makes sense anymore, the snapshot actually looks better now. So, disregard I suppose 😛
But yeah some matchers miss a
(T) -> Boolean
selector variant, like
fun <T, C : Collection<T>> C.shouldContain(t: T)
doesn’t have a
shouldContain { it % 2 == 0 }
variant like
shouldHaveSingleElement
does
s
we should definitely round out the missing blocks/predicates
e
But like the issue I linked mentions, it's much nicer to be able to supply a block where you can use assertions than having to use a predicate, since we get better failure messages and can leverage all other assertions that exist on a single element 🙂
s
agreed
w
Yeah, also seems like a matcher is better than
(T) -> Boolean
too, since it’ll be more flexible. In fact, only a matcher would be required, and the basic
X shouldBe y
could be
x shouldBe eq(y)
, and the usual comparison would be just a handy helper
e
something to look at, for now.. do you think it's a good idea to wrap the
runTests
and implement the matcher contract like this? way too late here now, but I'll setup a draft PR tomorrow to facilitate discussion
👍🏻 1
j
Nice, I found Inspectors just by pure luck when poking around the API the other day, would be used way more if it followed the
should...
convention I believe