I have a test class that looks like this: ```clas...
# kotest
l
I have a test class that looks like this:
Copy code
class NetworkEitherCallAdapterTest : StringSpec({

  private lateinit var server: MockWebServer
  private lateinit var service: CallErrorTestClient

  beforeAny {
      server = MockWebServer()
      server.start()
      service = Retrofit.Builder()
      .baseUrl(server.url("/"))
      .addConverterFactory(GsonConverterFactory.create())
      .addCallAdapterFactory(EitherCallAdapterFactory.create())
      .build()
      .create(CallErrorTestClient::class.java)
  }
  afterAny { server.shutdown() }

  "should return ResponseMock for 200 with valid JSON" {
      server.enqueue(MockResponse().setBody("""{"response":"Arrow rocks"}"""))
      val body = service.getEither()
      body shouldBe ResponseMock("Arrow rocks").right()
  }

  // More tests...
I want to run the test class 3 times with different
service
instances (which will use different
ConverterFactory
each). What is the most readable way to achieve it? I don't want to parameterize every test, I want to parameterize the whole class and repeat it.
s
You could look at test factories.
l
Thank you for the quick an helpful answer! It looks exactly like what I need ❤️
s
cool 🙂
l
How can I make sure though that
server
and
service
are regenerated for each test inside the factory using
beforeAny
?
Basically I want to parameterize the test only on the
jsonConverterFactory
(which in my example actually should replace the hardcoded
GsonConverterFactory.create()
call). But then the
server
and
service
need to be recreated for every test inside the factory.
I my original tests they were `lateinit var`s. I guess I just have to make them nullable in the test factory and safe-call them with
?.
everytime.
s
test factories have beforeTest/beforeAny and it only applies to the tests in that factory
so you can instantiate a factory multiple times, passing in whatever parameters you want
yeah if you want to avoid ? or !!, nothing obvious springs to mind
we need a way to inject a fixture into a test case
l
Yes, it's actually logical that I have to make these `var`s nullable, as the test factory is a function and not a class, so I cannot really cheat using
lateinit
as before.
s
right
l
It works now with
!!
and I don't mind it.
s
you could pull the server out
and just have start / stop inside the before/after
l
True, thanks!
👍🏻 1
No 🙂
That's the point
s
ah because you change it
l
I want a new instance of the server in every test so that the server configuration doesn't leak between tests
s
I guess the server is immutable so you need the var
l
I want a new server in every test
s
yep
l
Because e.g. of such tests:
Copy code
"should return IOError when no response" {
    server!!.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.NO_RESPONSE })

    val body = service!!.getEither()

    body.shouldBeInstanceOf<Left<IOError>>()
      .value.cause.shouldBeInstanceOf<SocketTimeoutException>()
  }
Maybe there is a method to reset the server config every time, but it gives me more peace of mind just to have a new server for every test.
s
I guess the alternative would be to have a function that returns a server and call it inside your tests manually.
Copy code
"mytest" {
  withServer { server- -> 
  }
}
then do the setup/teardown yourself and don't use before/any
l
Yes, but that's a bit counterproductive. I rather use the provided test setup/teardown facilities than copy-paste it myself. I can live with a bit of ugliness caused by the
!!
operator.
Thank you for all your help, it's amazing how easy it is to do such complex test setup in Kotest.
s
yeah the power of "tests as functions" really
👍 1