Экземпляр изменения отражения Kotlin и все члены, которые используют экземпляр - PullRequest
0 голосов
/ 26 февраля 2019

Мы используем рефлексию, чтобы наши тесты могли запускаться в разных средах.

Типичный тест будет выглядеть так:

class TestClass {
  val environment: Environment = generateEnvironment("jUnit")
  val path: String = environment.path

  //Do test stuff
}

Мы используем рефлексию следующим образом:

class PostgresqlTest{
  val classList: List<KClass<*>> = listOf(TestClass::class)
  val postgresEnv = generateEnvironment("postgres")

  @TestFactory
  fun generateTests(): List<DynamicTest> = classList.flatMap { testClass ->
    val instance = testClass.createInstance()
    environmentProperty(testclass).setter.call(instance, postgresEnv)

    //<<generate the dynamic tests>>

  }

  fun environmentProperty(testClass: KClass<*>) = 
    testClass.memberProperties.find {
      it.returnType.classifier == Environment::class
  } as KMutableProperty<*>
}

Теперь у нас есть проблема, которая path != environment.path в PostgresqlTest

Я знаю, что это можно решить в TestClass с помощью lazy или get (), как это

class TestClass {
  val environment: Environment = generateEnvironment("jUnit")

  val path: String by lazy { environment.path }

  // OR

  val path: String get() = environment.path
}

Однако это кажется потенциальной ошибкой для будущих разработчиков, тем более что первый фрагмент кода будет работать в TestClass и не работать только для тестов, в которых перезаписана среда.

Какой самый чистый способ гарантировать, что path == environment.path при перезаписи свойства?

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

В итоге я создал новое тестовое задание в gradle для каждой среды:

task postgresqlIntegrationTest(type: Test, group: "Verification", description: "Runs integration tests on postgresql.") {
    dependsOn compileTestKotlin
    mustRunAfter test

    environment "env", "postgresql"

    useJUnitPlatform {
        filter {
            includeTestsMatching "*IT"
        }
    }
}

, где мой тестовый класс просто загружает среду следующим образом:

class TestClass {
  val environment: Environment = generateEnvironment(System.getenv("env") ?: "junit")
  //Do test stuff
}
0 голосов
/ 26 февраля 2019

В идеале, если вы используете инфраструктуру внедрения зависимостей (например, Dagger), вы бы хотели, чтобы тестовые классы просто вставляли Environment (что позволило бы ссылаться на путь среды только после его предоставления), например:

class TestClass {
  @Inject lateinit var environment: Environment
  private lateinit var path: String

  @Before fun setup() {
    // do injection here
    path = environment.path
  }
}

В противном случае, я думаю, что делегирование интерфейса может быть хорошим вариантом здесь и полностью исключает рефлексию.Например, создайте EnvironmentHost, который отображает свойства environment и path:

interface EnvironmentHost {
  var environment: Environment
  val path: String
}

Создайте здесь реализацию для тестовых классов:

class TestEnvironmentHost : EnvironmentHost {
  override var environment: Environment = generateEnvironment("jUnit")
  override val path: String
    get() = environment.path
}

Тестовые классы теперь могутвыглядеть так:

class TestClass : EnvironmentHost by TestEnvironmentHost() {
  @Test fun myTest() {
    val myPath = path
    val myEnvironment = environment
  }
}

И ваш тестовый завод может быть упрощен до:

@TestFactory
fun generateTests(): List<DynamicTests> = classList.flatMap { testClass ->
  val instance = testClass.createInstance()

  // Assign an environment if the test is an EnvironmentHost. If not,
  // you could choose to treat that as a failure and require the test
  // class to be an EnvironmentHost.
  (instance as? EnvironmentHost)?.environment = postgresEnv

  ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...