Is there any way to add a test using a `Listener` ...
# kotest
w
Is there any way to add a test using a
Listener
or
Extension
(or something else) such that the added test is always executed as the last test case? I have a handful of test classes which all need to perform the same test after other test cases have completed, to ensure that the tests clean up after themselves.
e
You can intercept each testcase with an extension and validate that the test has cleaned up, and change the result of the test otherwise
w
That sounds interesting... I only want to perform the check once, after all test cases in a class have finished.
I suppose I could do it in an
afterSpec
, but I'd rather it show up as a test.
l
e
yeah if you want it to show up as a test then I think a test factory is the best option
w
That would mean that each test class which needs this check applied would have to include that factory, right? And would have to include it after all the other tests are defined? Is that right?
l
I think yes. It seems possible, but as per your description I'm not completely sure I understood your usecase
w
I'm writing integration tests which interact with a database. The tests can be run in two different modes:
1. Isolated, using a containerised database that is empty to start, and will be destroyed after the tests. 2. Clustered, using a persistent database that exists prior to the test execution and will continue to exist afterwards.
I want to add a test case to all of these integration tests which fails if the tests have left any records behind in the database. This new test case has to run after all the other tests.
The problem I see with a test factory is that someone writing the integration test could easily forget to include the check. I would rather have it included as a listener which can be added by the integration test parent class.
l
It seems like an AfterTest/AfterSpec of some sort indeed. Could also be put in
afterProject
, with less granularity
e
I think it would be a lot more convenient to add this is a check with a
TestCaseExtension
that modifies the outcome of the test if it doesn't clean up properly. You would immediately see which test case left lingering data (rather than just which Spec) and you could register it globally for your integration tests
Otherwise I suppose you could write a base class for your integration tests which mimics a spec, but also adds this test implicitly.
Copy code
abstract class DbSpec(function: FunSpec.() -> Unit) : FunSpec(
   {
      function()
      test("db should be empty"){}
   },
)
w
That would be a really nice approach if each test case cleaned up after itself, however, that's rarely the case in these integration tests. Instead, a user story is written as many test cases executed in order. So only at the end of the spec do we expect the database to have been cleaned up.
I think that base class suggestion would work. It's very similar to what I was considering but just different enough. I was going to do something very similar with an
init
block rather than a lambda, but that wouldn't work if a child class defined their tests in their own
init
block, since the child
init
would run after the parent's
init
block, causing the child's test cases to follow the one defined by the parent. But I think using a lambda as in your example would work.
e
Yeah I think it should do the trick 🙂
w
Thanks to both of you.
e
Only downside is that its hard to enforce that its used where appropiate, but perhaps you could leverage some static analysis tool to check that
w
Yeah, I don't think that'll be too much of an issue. I already have a base class that does a lot of important thinks for the integration tests (like manage that containerised database), so a spec won't be able to do much without using the base class.
👍 1
sad panda That base class method doesn't quite work. The lambda body gets executed before a child class's
init
block, so if the child uses an init block rather than a lambda constructor parameter, its tests get executed after the one added by the base class.
e
d'oh.. didnt think of that.. 😞
w
I think I've found a pretty good solution based on your suggestion to use a
TestCaseExtension
. I only run the check on root tests, that way anything within a test container can share database entries as long as they get cleaned up by the end of the container.