Kotlin反射更改实例以及使用该实例的所有成员

时间:2019-02-26 17:16:49

标签: reflection kotlin

我们正在使用反射使我们的测试能够在不同的环境中开始。

典型的测试如下:

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<*>
}

现在我们在PostgresqlTest中遇到了path != environment.path

的问题

我知道可以使用类似lazy或get()的方法在TestClass中解决此问题

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 :(得分:0)

理想情况下,如果您使用的是依赖项注入框架(例如Dagger),则希望测试类仅注入Environment(这将仅在提供环境路径之后才引用它),例如:

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

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

否则,我认为接口委托在这里可能是一个不错的选择,并且完全避免了反思。例如,创建一个显示EnvironmentHostenvironment属性的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

  ...
}

答案 1 :(得分:0)

我最终为每种环境在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
}