https://kotlinlang.org logo
#kotest
Title
# kotest
w

Wesley Hartford

11/14/2023, 5:09 PM
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

Emil Kantis

11/14/2023, 5:12 PM
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

Wesley Hartford

11/14/2023, 5:13 PM
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

LeoColman

11/14/2023, 6:50 PM
e

Emil Kantis

11/14/2023, 6:57 PM
yeah if you want it to show up as a test then I think a test factory is the best option
w

Wesley Hartford

11/14/2023, 6:59 PM
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

LeoColman

11/14/2023, 7:00 PM
I think yes. It seems possible, but as per your description I'm not completely sure I understood your usecase
w

Wesley Hartford

11/14/2023, 7:01 PM
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

LeoColman

11/14/2023, 7:07 PM
It seems like an AfterTest/AfterSpec of some sort indeed. Could also be put in
afterProject
, with less granularity
e

Emil Kantis

11/14/2023, 7:10 PM
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

Wesley Hartford

11/14/2023, 7:13 PM
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

Emil Kantis

11/14/2023, 7:30 PM
Yeah I think it should do the trick 🙂
w

Wesley Hartford

11/14/2023, 7:30 PM
Thanks to both of you.
e

Emil Kantis

11/14/2023, 7:30 PM
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

Wesley Hartford

11/14/2023, 7:32 PM
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

Emil Kantis

11/14/2023, 8:02 PM
d'oh.. didnt think of that.. 😞
w

Wesley Hartford

11/14/2023, 9:03 PM
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.