Модуль Patch Java 9 с тест-кодом для работы с отражениями - PullRequest
5 голосов
/ 06 февраля 2020

Как добавить свои тесты в производственный код во время выполнения теста, чтобы оба они были в одном модуле Java 9 и могли получать доступ друг к другу с помощью отражений?

Я пробовал до сих пор:

  • Удалите модульность Java 9 (на самом деле module-info.java) → она работала отлично, но это не то, что мне нужно.
  • Переместите мои тесты в выделенный модуль (и, следовательно, также пакет) → он работал отлично, но это не то, что я ищу. (Я хочу, чтобы мои юнит-тесты были рядом с моим кодом.)
  • Используйте --patch-module для виртуального добавления другой папки (к указанной с помощью --module-path) → она работала с «обычным» кодом, но не с отражениями, он не находит классы, указанные --module-path.
    • Укажите и мой тестовый, и рабочий код, используя --patch-module → он находит классы только в указанной мной папке.
    • Явно добавьте --add-opens mymodule/mypackge=mymodule или ...=ALL-UNNAMED, чтобы открыть его для отражения → не похоже, чтобы это имело эффект.

Итак, моя полная тестовая строка:

java \
  --patch-module com.stackoverflow.examplemodule=ModuleInfoTest:ModuleInfoExample \
  --module-path ModuleInfoExample \
  --add-opens com.stackoverflow.examplemodule/com.stackoverflow.examplepackage=com.stackoverflow.examplemodule \
  --add-opens com.stackoverflow.examplemodule/com.stackoverflow.examplepackage=ALL-UNNAMED \
  --module com.stackoverflow.examplemodule/com.stackoverflow.examplepackage.Main

Я нахожусь в каталоге, содержащем следующие подкаталоги и файлы:

  • ModuleInfoExample / module-info.class
  • ModuleInfoExample / com / stackoverflow / examplepackage / Main.class
  • ModuleInfoTest / com / stackoverflow / examplepackage / AnyClass.class

Я использую:

openjdk version "13.0.2" 2020-01-14
OpenJDK Runtime Environment (build 13.0.2+8)
OpenJDK 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)



Как я узнал из утвержденного ответа, моя проблема не была действительно " доступ другие классы", как я это сформулировал. Но больше похоже на найдите их (путем сканирования classpath / modulepath). Однако эта часть уже получила ответ в этом другом вопросе StackOverflow .

1 Ответ

5 голосов
/ 08 февраля 2020

С Oracle Java 13 java команда это две опции, которые вы пытались использовать:

--add-opens module/package=target-module(,target-module)*
--patch-module module=file(;file)*

но:

  • --add-opens не поможет вам, потому что он открывает один модуль для других модулей. У вас есть только один модуль.
  • --patch-module должен указывать каталоги (или файлы jar), которые вы хотите вставить в модуль. Я заметил, что есть ;, а не :, как вы использовали. Мне кажется, что вы говорили java исправлять файлы из того же каталога, где находится ваш модуль, с помощью :ModuleInfoExample.

Вам нужно только добавить файлы из ModuleInfoTest/ в ваш модуль. Я создал структуру из вашего Вопроса и запустил ее:

Компиляция:

javac -d target/ModuleInfoExample src/ModuleInfoExample/*.java src/ModuleInfoExample/com/stackoverflow/examplepackage/*.java
javac -cp target/ModuleInfoExample -d target/ModuleInfoTest src/ModuleInfoTest/com/stackoverflow/examplepackage/*.java

Запуск Main из модуля - без добавления классов:

java --module-path target/ModuleInfoExample --module com.stackoverflow.examplemodule/com.stackoverflow.examplepackage.Main

prints:
Hello world - I'm private

Запуск AnyClass из модуля - нет добавленных классов - ожидается исключение

java --module-path target/ModuleInfoExample --module com.stackoverflow.examplemodule/com.stackoverflow.examplepackage.AnyClass

Error: Could not find or load main class com.stackoverflow.examplepackage.AnyClass in module com.stackoverflow.examplemodule

Запуск AnyClass из модуля - добавление AnyClass в пакет:

java --module-path target/ModuleInfoExample --patch-module com.stackoverflow.examplemodule=target/ModuleInfoTest --module com.stackoverflow.examplemodule/com.stackoverflow.examplepackage.AnyClass

prints:
Inside AnyClass - calling Main: Hello world - I'm private

  field.get() = I'm private
  field.get() = I'm not private anymore

Общая структура:

>tree /f
..snip..
C:.
+---src
¦   +---ModuleInfoExample
¦   ¦   ¦   module-info.java
¦   ¦   ¦
¦   ¦   +---com
¦   ¦       +---stackoverflow
¦   ¦           +---examplepackage
¦   ¦                   Main.java
¦   ¦
¦   +---ModuleInfoTest
¦       +---com
¦           +---stackoverflow
¦               +---examplepackage
¦                       AnyClass.java
¦
+---target
    +---ModuleInfoExample
    ¦   ¦   module-info.class
    ¦   ¦
    ¦   +---com
    ¦       +---stackoverflow
    ¦           +---examplepackage
    ¦                   Main.class
    ¦
    +---ModuleInfoTest
        +---com
            +---stackoverflow
                +---examplepackage
                        AnyClass.class

src \ ModuleInfoExample \ module-info. java:

module com.stackoverflow.examplemodule {
//  exports com.stackoverflow.examplepackage; // no need to export. Nothing is using this
}

src \ ModuleInfoExample \ com \ stackoverflow \ examplepackage \ Main. java:

package com.stackoverflow.examplepackage;

public class Main {
  private String privateString = "I'm private";

  public static void main(String[] args) {
    new Main().hello();
  }
  public void hello(){
    System.out.println("Hello world - " + privateString);
  }
}

src \ ModuleInfoTest \ com \ stackoverflow \ examplepackage \ AnyClass. java:

package com.stackoverflow.examplepackage;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class AnyClass {
  public static void main(String[] args) {
    testhello();
    System.out.println();
    breakhello();
  }

  public static void testhello(){
    System.out.print("Inside AnyClass - calling Main: ");
    Main test = new Main();
    test.hello();
  }

  public static void breakhello(){
    try {
      // Not necessary - same package, but..
      Class<?> mainClass = Class.forName("com.stackoverflow.examplepackage.Main");
      Constructor<?> constructor = mainClass.getConstructor();
      Object main = constructor.newInstance();

      // Getting, printing and changing the field..
      Field field = mainClass.getDeclaredField("privateString");
      field.setAccessible(true);
      System.out.println("  field.get() = " + field.get(main));
      field.set(main,"I'm not private anymore");
      System.out.println("  field.get() = " + field.get(main));

    } catch (Exception e) {  // Sorry, all in one big bucket
       System.out.println("Error: " + e);
    }
  }
}
...