Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not work on JDK 16+ due to JEP-396 (strong encapsulation) #27

Open
kriegaex opened this issue Aug 14, 2021 · 0 comments
Open

Does not work on JDK 16+ due to JEP-396 (strong encapsulation) #27

kriegaex opened this issue Aug 14, 2021 · 0 comments

Comments

@kriegaex
Copy link

kriegaex commented Aug 14, 2021

de.jodamob.kotlin.testrunner.NoMoreFinalsClassLoader uses Javassist which tries to make protected java.lang.ClassLoader.defineClass accessible. Due to JEP-396, this is forbidden since JDK 16 and cannot be overriden by any --add-opens call either.

Please update the product in order to use another API.


FYI, I use Spock and my workaround for ignoring tests using SpotlinTestRunner on JDK 16+ looks as follows, using a delegation pattern:

package de.scrum_master.testing

import de.jodamob.kotlin.testrunner.SpotlinTestRunner
import org.junit.runner.Description
import org.junit.runner.manipulation.Filter
import org.junit.runner.manipulation.NoTestsRemainException
import org.junit.runner.manipulation.Sorter
import org.junit.runner.notification.RunNotifier
import org.junit.runners.model.InitializationError
import org.spockframework.runtime.Sputnik

class JEP396AwareSpotlinTestRunner extends Sputnik {
  private static final int javaMajor = Integer.parseInt(System.properties.getProperty("java.version").split("[.]")[0])
  public static final boolean hasJEP396 = javaMajor >= 16

  private static final Filter ignoreAllFilter = new Filter() {
    @Override
    boolean shouldRun(Description description) { return false }

    @Override
    String describe() { return "ignores all tests" }
  }

  private final Sputnik sputnik

  JEP396AwareSpotlinTestRunner(Class<?> clazz) throws InitializationError {
    super(clazz)
    sputnik = hasJEP396 ? this : new SpotlinTestRunner(clazz)
  }

  @Override
  void filter(Filter filter) throws NoTestsRemainException {
    if (hasJEP396)
      super.filter(ignoreAllFilter)
    else
      sputnik.filter(filter)
  }

  @Override
  void run(RunNotifier notifier) {
    if (hasJEP396)
      super.run(notifier)
    else
      sputnik.run(notifier)
  }

  @Override
  Description getDescription() {
    if (hasJEP396)
      return super.getDescription()
    else
      return sputnik.getDescription()
  }

  @Override
  void sort(Sorter sorter) {
    if (hasJEP396)
      super.sort(sorter)
    else
      sputnik.sort(sorter)
  }
}

The Spock spec still needs an @IgnoreIf or @Requires in addition to @RunWith(JEP396AwareSpotlinTestRunner), but otherwise no changes are required. It can even utilise a constant JEP396AwareSpotlinTestRunner.hasJEP396 for its own condition:

package de.scrum_master.stackoverflow

import de.jodamob.kotlin.testrunner.OpenedClasses
import de.jodamob.kotlin.testrunner.OpenedPackages
import de.scrum_master.testing.JEP396AwareSpotlinTestRunner
import org.junit.runner.RunWith
import spock.lang.IgnoreIf
import spock.lang.Specification

/**
 * See https://stackoverflow.com/q/48391716/1082681
 * See https://github.com/dpreussler/kotlin-testrunner
 */
@RunWith(JEP396AwareSpotlinTestRunner)
@IgnoreIf({ JEP396AwareSpotlinTestRunner.hasJEP396 })
@OpenedClasses(FinalClass)
//@OpenedPackages("de.scrum_master.stackoverflow")
class AnotherClassSpotlinRunnerTest extends Specification {
  def "use SpotlinRunner to stub final method in final class"() {
    given:
    FinalClass finalClass = Stub() {
      finalMethod() >> "mocked"
    }

    expect:
    new AnotherClass().doSomething(finalClass) == "mocked"
  }
}

The workaround is necessary, because JUnit 4 runners are initialised very early in the lifecycle, even before SpockConfig.groovy or global Spock extensions, even more so before @IgnoreIf and @Requires can kick in.

This is ugly, of course. There are alternatives to the Kotlin test runner, e.g. my own tool Sarek which can do a lot of fancy things, unfinalising classes for JUnit 4/5, TestNG and Spock just being one of them. But before I developed it, I was using your tool in Spock 1.3 and today just noticed that my old example code was no longer when running on JDK 16. So I thought, I take some time to create this issue, FWIW.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant