Как получить доступ к полям тестового класса с помощью правила в JUnit

Я хочу написать JUnit @Rule (версия 4.10), чтобы настроить некоторые объекты (Entity Manager) и сделать их доступными в тесте, путем "впрыскивания" его в переменную.

Что-то вроде этого:

public class MyTestClass() {

  @Rule
  MyEntityManagerInjectRule = new MyEntityManagerInjectRule():

  //MyEntityManagerInjectRule "inject" the entity manager
  EntityManger em;

  @Test...
}

Проблема в том, что я не знаю, как получить текущий экземпляр MyTestClass в MyEntityManagerInjectRule (расширяет TestRule), потому что он имеет только один метод. Statement apply(Statement base, Description description);

Внутри описания есть только класс MyTestClass, но не экземпляр, используемый для теста.

Альтернативой будет использование org.junit.rules.MethodRule, но это устарело. До и после недостаточно для этой задачи, потому что тогда мне нужно будет скопировать код в тесты, и они более или менее устарели. (См. Block4JClassRunner.withBefores/withAfters).

Итак, мой вопрос в том, как я могу получить доступ к экземпляру тестового класса без использования устаревшего материала.

Ответ 1

Как насчет:

public class MyTestClass() {
  @Rule
  public TestRule MyEntityManagerInjectRule =
         new MyEntityManagerInjectRule(this); // pass instance to constructor

  //MyEntityManagerInjectRule "inject" the entity manager
  EntityManger em;

  @Test...
}

Просто добавьте экземпляр тестового класса в конструктор для @Rule. Просто будьте осторожны с порядком назначения.

Ответ 2

Правильный способ добиться этого - сделать ваше правило реализацией org.junit.rules.MethodRule (not TestRule). Метод apply() интерфейса MethodRule имеет аргумент target, который содержит ссылку на текущий экземпляр тестового класса (конечно, это другой экземпляр каждый раз, когда метод выполняется).

Пример MethodRule

public class ExampleMethodRule implements MethodRule {

    @Override
    public Statement apply(final Statement base,
                                         FrameworkMethod method, Object target) {
        System.out.println("ExampleMethodRule#apply()" +
                           "\n\t base: " + base +
                           "\n\t method (current test method): " + method +
                           "\n\t target (current test class instance): "+target);

        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                System.out.println("->before evaluate()");
                try {
                    base.evaluate();
                } finally {
                    System.out.println("->after evaluate()");
                }
            }
        };
    }
}

Пример тестового класса с использованием @Rule

public class ExampleTest {

    @Rule
    public ExampleMethodRule exampleMethodRule = new ExampleMethodRule();

    @BeforeClass
    public static void beforeClass() {
        System.out.println("@BeforeClass");
    }
    @AfterClass
    public static void afterClass() {
        System.out.println("@AfterClass");
    }

    @Before
    public void before() {
        System.out.println("@Before");
    }
    @After
    public void after() {
        System.out.println("@After");
    }

    @Test
    public void aa() {
        System.out.println("method aa()");
    }
    @Test
    public void bb() {
        System.out.println("method bb()");
    }

}

Ответ 3

Существует другой подход: правило предоставляет EntityManager вместо того, чтобы вводить его.

public class MyTestClass() {
  @Rule
  public MyEntityManagerRule rule = new MyEntityManagerRule();

  @Test
  public void firstTest() {
     doSomethingWith(rule.getEntityManager());
  }
}